#
# $Id: Jamroot.jam 2403 2010-12-02 21:33:35Z chambm $
#
#
# Original author: Darren Kessner <darren@proteowizard.org>
#
# Copyright 2008 Spielberg Family Center for Applied Proteomics
#   Cedars-Sinai Medical Center, Los Angeles, California  90048
#
# Licensed under the Apache License, Version 2.0 (the "License"); 
# you may not use this file except in compliance with the License. 
# You may obtain a copy of the License at 
# 
# http://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing, software 
# distributed under the License is distributed on an "AS IS" BASIS, 
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
# See the License for the specific language governing permissions and 
# limitations under the License.


.os = [ modules.peek : OS ] ; # NT, LINUX, MACOSX
.platform = [ modules.peek : OSPLAT ] ; # X86, X86_64, POWERPC

constant BOOST_VERSION : 1 43 0 ;

local default_libraries_path = "./libraries" ;
local libraries_path = [ MATCH --libraries-path=(.*) : [ modules.peek : ARGV ] ] ;
libraries_path ?= $(default_libraries_path) ; # set default path in absence of command-line path

local default_boost_src = "$(libraries_path)/boost_$(BOOST_VERSION:J=_)" ;
local boost_src = [ MATCH --boost-src=(.*) : [ modules.peek : ARGV ] ] ;
boost_src ?= $(default_boost_src) ; # set default path in absence of command-line path

local default_zlib_src = "$(libraries_path)/zlib-1.2.3" ;
local zlib_src = [ MATCH --zlib-src=(.*) : [ modules.peek : ARGV ] ] ;
zlib_src ?= $(default_zlib_src) ; # set default path in absence of command-line path


path-constant PWIZ_ROOT_PATH : . ;
path-constant PWIZ_BUILD_PATH : build-$(.os:L)-$(.platform:L) ;
path-constant PWIZ_LIBRARIES_PATH : $(libraries_path) ;


import testing ; # needed to enable unit-test rule
import feature ; # needed for install rules
import package ; # needed for package install

if --teamcity-test-decoration in [ modules.peek : ARGV ]
{
    if [ modules.peek : NT ]
    {
        TEST_PRECOMMAND = "set pp=##\necho %pp%teamcity[testStarted name='%name%']" ;
        TEST_POSTCOMMAND =
            "set pp=##\n"
            "IF %status% NEQ 0"
                "(echo %pp%teamcity[testFailed name='%name%' message='Exit status: %status%'] )\n" # TODO: does testFailed go to stderr? 1>&2
            "echo %pp%teamcity[testFinished name='%name%']" ;
        TEST_POSTCOMMAND = $(TEST_POSTCOMMAND:J=" ") ;
    }
    else
    {
        TEST_PRECOMMAND = "pp=##\necho $pp\"teamcity[testStarted name='$name']\"" ;
        TEST_POSTCOMMAND = 
            "pp=##"
            "if test $status -ne 0 ; then"
                "echo $pp\"teamcity[testFailed name='$name' message='Exit status: $status']\" ;"
            "fi"
            "echo $pp\"teamcity[testFinished name='$name']\"" ;
        TEST_POSTCOMMAND = $(TEST_POSTCOMMAND:J=\n) ;
    }
    
    # put raw commands in global module
    modules.poke : TEST_PRECOMMAND : $(TEST_PRECOMMAND) ;
    modules.poke : TEST_POSTCOMMAND : $(TEST_POSTCOMMAND) ;
    
    # prepend commands with grist expected by testing.jam
    TEST_PRECOMMAND = <testing.precommand>$(TEST_PRECOMMAND) ;
    TEST_POSTCOMMAND = <testing.postcommand>$(TEST_POSTCOMMAND) ;
}

local msvc-secure-scl = ;
if --without-secure-scl in [ modules.peek : ARGV ]
{
    # TODO: change this from a <define> to a feature in msvc.jam
    # disable checked iterators (on by default in VC8/9)
    msvc-secure-scl = <toolset>msvc,<variant>release:<define>_SECURE_SCL=0 ;
}


################################
## Targets and Related Options #
################################
#   build                   Build ProteoWizard applications
#   =====
#   --libraries-path=<DIR>  Find libraries directory here.
#                           Default: "./libraries"
#
#   --boost-src=<DIR>       Find Boost source distribution here.
#                           Default: "<libraries-path>/boost_*"
#
#   --zlib-src=<DIR>        Find zlib source distribution here.
#                           Default: "<libraries-path>/zlib-1.23.0"
project pwiz 
    : requirements 
        <include>$(PWIZ_ROOT_PATH)
        <include>$(PWIZ_LIBRARIES_PATH)/boost_aux
        <include>$(boost_src)
        <include>$(zlib_src)

        <toolset>gcc,<link>shared:<runtime-link>shared
        <toolset>darwin,<link>shared:<runtime-link>shared

        # any module which links with .NET (either dynamically or statically) must use the shared runtime
        <toolset>msvc:<runtime-link>shared

        # SEH exceptions crossing native/managed boundaries are problematic with this set to off;
        # also, catch(...) will catch SEH exceptions with this on
        <toolset>msvc:<asynch-exceptions>on

        # special msvc hacks
        <toolset>msvc:<define>WIN32                     # windows
        <toolset>msvc:<define>_CRT_SECURE_NO_DEPRECATE  # don't deprecate the standard library 
        <toolset>msvc:<define>_SCL_SECURE_NO_DEPRECATE  # don't deprecate the standard library 
        <toolset>msvc:<define>_SECURE_SCL_THROWS=1      # checked iterators throw instead of crash  
        <toolset>msvc:<define>_USE_MATH_DEFINES         # for M_PI in <cmath>
        <toolset>msvc:<cxxflags>/wd4100                 # warning: unreferenced formal parameter
        <toolset>msvc:<cxxflags>/wd4512                 # warning: assignment operator could not be generated
        <toolset>msvc:<cxxflags>/wd4127                 # warning: conditional expression is constant (boost::lexical_cast)
        <toolset>msvc:<cxxflags>/wd4701                 # warning: potentially uninitialized local variable 'result' used (boost::lexical_cast, debug)
        <toolset>msvc:<cxxflags>/wd4245                 # warning: conversion from 'type1' to 'type2', signed/unsigned mismatch (boost/filesystem/convenience.hpp)
        <toolset>msvc:<cxxflags>/wd4251                 # warning: class needs to have dll-interface to be used by clients of class
        <toolset>msvc:<cxxflags>/wd4267	                # warning: conversion from 'type1' to 'type2', possible loss of data (boost::lexical_cast)
        <toolset>msvc:<cxxflags>/wd4244                 # warning: conversion from 'int' to 'unsigned short', possible loss of data (boost/date_time/microsec_time_clock.hpp)
        <toolset>msvc:<cxxflags>/wd4275                 # warning: non dll-interface class 'base' used as base for dll-interface class 'derived'
        <toolset>msvc:<cxxflags>/wd4702                 # warning: unreachable code (boost::lexical_cast)

        # warning: _SECURE_SCL_THROWS is deprecated
        <toolset>msvc:<define>_SILENCE_DEPRECATION_OF_SECURE_SCL_THROWS

        $(msvc-secure-scl)

        # special gcc hack - Cygwin gcc 3.4.4, Ubuntu gcc 4.1.2 
        # warning: '__cur' might be used uninitialized in this function 
        <toolset>gcc:<cxxflags>-Wno-uninitialized   

        # special Cygwin gcc-3.4.4 hack
        # linker "multiple definition" error on inclusion of boost-1.34.1 filesystem headers  
        <toolset>gcc-3.4.4:<linkflags>-Wl,--allow-multiple-definition
        <toolset>gcc-mingw-3.4.5:<linkflags>-Wl,--allow-multiple-definition

        # allow "long long" even with -pedantic
        <toolset>gcc:<cxxflags>-Wno-long-long
        <toolset>darwin:<cxxflags>-Wno-long-long

        # any GCC executable that uses shared libraries must have all its code built with -fPIC
        <conditional>@static-with-fpic

        # don't call 'strip' -- causes 'Bus error' in some cases
        #  e.g. find_if with inline predicate
        <toolset>darwin:<debug-symbols>on
        
        # use of boost::thread requires multithreaded runtime
        <threading>multi

        $(TEST_PRECOMMAND)
        $(TEST_POSTCOMMAND)

    : build-dir $(PWIZ_BUILD_PATH)
    : usage-requirements
        <include>.
    : default-build
        release
        <link>static
        <runtime-link>static
        #<warnings-as-errors>on
        <warnings>all
        <threading>multi
    ;


# external library declarations

lib pthread 
    : # sources
    : # requirements
        <name>pthread  
        <link>shared
        <toolset>gcc:<linkflags>-pthread # sometimes segfault without this -- dk
    : # default-build
    : # usage-requirements
        <toolset>gcc:<linkflags>-pthread # sometimes segfault without this -- dk
    ;

lib jpeg png z : : <threading>multi ;
lib gd : jpeg png z : <name>gd <threading>multi ;
lib gd : : <toolset>msvc <name>bgd <search>$(PWIZ_LIBRARIES_PATH) : : <include>$(PWIZ_LIBRARIES_PATH)/gd-2.0.33 ;

lib fftw3 : : <threading>multi <search>$(PWIZ_LIBRARIES_PATH)/fftw-3.1.2/.libs : : <include>$(PWIZ_LIBRARIES_PATH)/fftw-3.1.2/api ;
lib fftw3 : : <threading>multi <toolset>msvc <name>libfftw3-3 <search>$(PWIZ_LIBRARIES_PATH) : : <include>$(PWIZ_LIBRARIES_PATH)/fftw-3.1.2/api ;

alias svm : $(PWIZ_LIBRARIES_PATH)/libsvm-3.0//svm ;

modules.poke : BOOST_BUILD_PATH : [ modules.peek : BOOST_BUILD_PATH ] $(PWIZ_LIBRARIES_PATH) ;


# get version info (used for tarball filenames)
import svnrev sequence common ;

constant PWIZ_MAJOR : 2 ;
constant PWIZ_MINOR : 1 ;

# rule for generating a ProteoWizard Version.cpp file
rule generate-Version.cpp ( filepath ? : namespace ? : sources-with-rcs-keywords + :
                            warn-on-missing ? : print-revision-info ? )
{
    # default filepath is "Version.cpp" in the current directory
    filepath ?= Version.cpp ;
    filepath = [ path.native $(filepath) ] ;

    if $(print-revision-info) { echo Generating/updating version in $(filepath) ; }

    local existing-revision-info ;
    if [ path.exists $(filepath) ]
    {
        existing-revision-info = [ svnrev.get-revision-info $(filepath) ] ;
        if $(print-revision-info) { echo Existing maximum revision: $(existing-revision-info) ; }
    }

    sources-with-rcs-keywords = [ sequence.transform path.native : $(sources-with-rcs-keywords) ] ;
    local revision-info = [ svnrev.get-revision-info $(sources-with-rcs-keywords)
                                : $(warn-on-missing) : $(print-revision-info) ] ;

    if $(print-revision-info) { echo Current maximum revision: $(revision-info) ; }

    local namespace-header = "" ;
    local namespace-footer = "" ;
    if $(namespace)
    {
        namespace-header = "namespace $(namespace) {\n" ;
        namespace-footer = "\n} // namespace $(namespace)" ;
    }

    if ! $(existing-revision-info) ||
       $(existing-revision-info[1]) != $(revision-info[1])
    {
        if [ path.exists $(filepath) ]
        {
            local rm = [ common.rm-command ] ;
            rm = $(rm:J=" ") ;
            SHELL "$(rm) $(filepath)" ;
        }

        local header-text =
"// This file was generated by the \"svnrev\" utility
// You should not modify it manually, as it may be re-generated.
//
// $Revision: $(revision-info[1]) $
// $Date: $(revision-info[2])-$(revision-info[3])-$(revision-info[4]) $
//

\#define PWIZ_SOURCE
\#include \"Version.hpp\"
\#include <sstream>

\#ifdef PWIZ_USER_VERSION_INFO_H // in case you need to add any info version of your own
\#include PWIZ_USER_VERSION_INFO_H  // must define PWIZ_USER_VERSION_INFO_H_STR for use below
\#endif

namespace pwiz {
$(namespace-header)

using std::string;


int Version::Major()                {return $(PWIZ_MAJOR);}
int Version::Minor()                {return $(PWIZ_MINOR);}
int Version::Revision()             {return $(revision-info[1]);}
string Version::LastModified()      {return \"$(revision-info[2])-$(revision-info[3])-$(revision-info[4])\";}
string Version::str()               
{
	std::ostringstream v;
	v << Major() << '.' << Minor() << '.' << Revision();
\#ifdef PWIZ_USER_VERSION_INFO_H
	v << \" (\" << PWIZ_USER_VERSION_INFO_H_STR << \")\";
\#endif
	return v.str();
}

$(namespace-footer)
} // namespace pwiz
" ;

        header-text = @($(filepath):E=$(header-text)) ;
    }

    return $(revision-info) ;
}


generate-Version.cpp $(PWIZ_ROOT_PATH)/pwiz/data/msdata/Version.cpp : msdata :
    [ path.glob $(PWIZ_ROOT_PATH)/pwiz/data/msdata
                $(PWIZ_ROOT_PATH)/pwiz/data/vendor_readers/Agilent
                $(PWIZ_ROOT_PATH)/pwiz/data/vendor_readers/Thermo
                $(PWIZ_ROOT_PATH)/pwiz/utility/misc
                $(PWIZ_ROOT_PATH)/pwiz/utility/minimxml
                $(PWIZ_ROOT_PATH)/pwiz/utility/vendor_api/Agilent
                $(PWIZ_ROOT_PATH)/pwiz/utility/vendor_api/thermo
                $(PWIZ_ROOT_PATH)/pwiz_aux/msrc/data/vendor_readers/ABI
                $(PWIZ_ROOT_PATH)/pwiz_aux/msrc/data/vendor_readers/Bruker
                $(PWIZ_ROOT_PATH)/pwiz_aux/msrc/data/vendor_readers/Waters
                $(PWIZ_ROOT_PATH)/pwiz_aux/msrc/utility/vendor_api/ABI
                $(PWIZ_ROOT_PATH)/pwiz_aux/msrc/utility/vendor_api/Bruker
                $(PWIZ_ROOT_PATH)/pwiz_aux/msrc/utility/vendor_api/Waters
       : *.?pp Jamfile.jam : Version.cpp
    ] ;

generate-Version.cpp $(PWIZ_ROOT_PATH)/pwiz/analysis/Version.cpp : analysis :
    [ path.glob-tree $(PWIZ_ROOT_PATH)/pwiz/analysis
                     $(PWIZ_ROOT_PATH)/pwiz/utility/math
                     $(PWIZ_ROOT_PATH)/pwiz/utility/misc
                     $(PWIZ_ROOT_PATH)/pwiz/utility/minimxml
        : *.?pp Jamfile.jam : Version.cpp
    ] ;

generate-Version.cpp $(PWIZ_ROOT_PATH)/pwiz/data/mziddata/Version.cpp : mziddata :
    [ path.glob-tree $(PWIZ_ROOT_PATH)/pwiz/data/mziddata
                     $(PWIZ_ROOT_PATH)/pwiz/utility/misc
                     $(PWIZ_ROOT_PATH)/pwiz/utility/minimxml
        : *.?pp Jamfile.jam : Version.cpp
    ] ;

generate-Version.cpp $(PWIZ_ROOT_PATH)/pwiz/data/tradata/Version.cpp : tradata :
    [ path.glob-tree $(PWIZ_ROOT_PATH)/pwiz/data/tradata
                     $(PWIZ_ROOT_PATH)/pwiz/utility/misc
                     $(PWIZ_ROOT_PATH)/pwiz/utility/minimxml
        : *.?pp Jamfile.jam : Version.cpp
    ] ;

generate-Version.cpp $(PWIZ_ROOT_PATH)/pwiz/data/proteome/Version.cpp : proteome :
    [ path.glob $(PWIZ_ROOT_PATH)/pwiz/data/proteome : *.?pp Jamfile.jam : Version.cpp ] ;

# TODO: collecting global revision info should use revision info that's already been collected;
# TODO: especially for things like SeeMS which have some MSVC-generated files that aren't versioned
local revision-info =
    [ generate-Version.cpp $(PWIZ_ROOT_PATH)/pwiz/Version.cpp : :
            $(PWIZ_ROOT_PATH)/Jamroot.jam
            [ path.glob $(PWIZ_ROOT_PATH)/libraries : *.jam ]
            [ path.glob-tree $(PWIZ_ROOT_PATH)/pwiz
                             $(PWIZ_ROOT_PATH)/pwiz_aux/msrc
                             $(PWIZ_ROOT_PATH)/pwiz_tools/commandline
                             $(PWIZ_ROOT_PATH)/pwiz_tools/common
                             $(PWIZ_ROOT_PATH)/pwiz_tools/examples
                             $(PWIZ_ROOT_PATH)/pwiz_tools/SeeMS
                             $(PWIZ_ROOT_PATH)/pwiz_tools/sld
                : *.?pp *.jam *.cs
                : tar.excluded Version.cpp
            ]
        : warn-on-missing
        #: print-revision-info
    ] ;

# revision-info is a sequence: <max revision> <max year> <max month> <max day> <number of modified files in working copy>

local year = $(revision-info[2]) ;
local month = $(revision-info[3]) ;
local day = $(revision-info[4]) ;

constant PWIZ_SVNREV : $(revision-info[1]) ;
constant PWIZ_SVNREVDATE : "(last committed change: $(year)-$(month)-$(day))" ;

version-tag = $(PWIZ_MAJOR) $(PWIZ_MINOR) $(PWIZ_SVNREV) ;

if $(revision-info[5]) > 0
{
    version-tag += "modified" ;
    echo NOTICE: WORKING COPY HAS $(revision-info[5]) MODIFIED FILES. ;
}

echo ProteoWizard $(version-tag:J=.) $(PWIZ_SVNREVDATE:J=) ;


# create a VERSION file which can be used by TC to parse the canonical pwiz version
make VERSION : : @make_VERSION : <location>$(PWIZ_BUILD_PATH) ;
actions make_VERSION { @($(STDOUT):E=$(version-tag:J=.)) > "$(<)" }


import tar os ;

if [ os.on-windows ]
{
    using tar : bsdtar.exe ;
}
else if $(.os:L) = "macosx" || $(.os:L) = "darwin"
{
    using tar : gnutar ;
}
else
{
    using tar ;
}


tar.extract $(PWIZ_LIBRARIES_PATH)/gd-2.0.33.tar.bz2 : *.h : <location>$(PWIZ_LIBRARIES_PATH) ;

path-constant BOOST_SOURCE : $(boost_src) ;
path-constant ZLIB_SOURCE : $(zlib_src) ;

using ext-boost : $(BOOST_VERSION:J=.) : $(BOOST_SOURCE) : <zlib-src-location>$(ZLIB_SOURCE) ;

if [ modules.peek : NT ]
{
    import path ;
    
    # HACK: bug in boost::iostreams 1.43 requires a patch to support building with link=shared
    # Boost bug ticket: https://svn.boost.org/trac/boost/ticket/4335
    SHELL "@copy /Y $(PWIZ_LIBRARIES_PATH)\\boost_aux\\file_descriptor.hpp $(PWIZ_LIBRARIES_PATH)\\boost_1_43_0\\boost\\iostreams\\device" ;

    # HACK: bug in boost::thread 1.43 requires a patch for thread-safe static usage of call_once (i.e. singletons)
    # Boost bug ticket: https://svn.boost.org/trac/boost/ticket/4225
    SHELL "@copy /Y $(PWIZ_LIBRARIES_PATH)\\boost_aux\\once.hpp $(PWIZ_LIBRARIES_PATH)\\boost_1_43_0\\boost\\thread\\win32" ;
    SHELL "@copy /Y $(PWIZ_LIBRARIES_PATH)\\boost_aux\\thread_primitives.hpp $(PWIZ_LIBRARIES_PATH)\\boost_1_43_0\\boost\\thread\\win32" ;
}


rule project-exists ( project-path )
{
    if [ path.exists $(project-path) ] &&
       [ path.glob $(project-path) : [Jj]amroot.jam [Jj]amfile.jam [Jj]amroot [Jj]amfile ]
    {
        return true ;
    }
    else
    {
        return ;
    }
}

# to make subsetting the source tree much easier,
# use these rules to test that a sub-project path exists before building it
rule build-project-if-exists ( project-path )
{
    local project = [ project.current ] ;
    local p = [ path.native [ path.join [ $(project).location ] $(project-path) ] ] ;
    if [ project-exists $(p) ]
    {
        local attributes = [ project.attributes [ $(project).name ] ] ;
        local now = [ $(attributes).get projects-to-build ] ;
        $(attributes).set projects-to-build : $(now) $(project-path) ;
    }
}

rule run-if-exists ( sources + : args * : input-files * : requirements * : target-name ? : default-build * )
{
    local project = [ project.current ] ;
    local full-path = [ path.native [ path.join [ $(project).location ] $(project-path) $(sources[1]) ] ] ;
    if [ path.exists $(full-path) ]
    {
        return [ run $(sources) : $(args) : $(input-files) : $(requirements) : $(target-name) : $(default-build) ] ;
    }
}

rule unit-test-if-exists ( target : sources + : properties * )
{
    local project = [ project.current ] ;
    local full-path = [ path.native [ path.join [ $(project).location ] $(project-path) $(sources[1]) ] ] ;
    if [ path.exists $(full-path) ]
    {
        return [ unit-test $(target) : $(sources) : $(properties) ] ;
    }
}


# build/install

build-project-if-exists pwiz ;
build-project-if-exists pwiz_aux ;
build-project-if-exists pwiz_tools ;


rule install-location ( properties * )
{
    local toolsets = [ feature.get-values <toolset> : $(properties) ] ;
    local variants = [ feature.get-values <variant> : $(properties) ] ;
    if <link>shared in $(properties)
    {
        return <location>$(PWIZ_BUILD_PATH)/$(toolsets[1])-$(variants[1])-shared ;
    }
    else
    {
        return <location>$(PWIZ_BUILD_PATH)/$(toolsets[1])-$(variants[1]) ;
    }
}

rule install-type ( properties * )
{
    local result = <install-type>EXE ;
    if <link>shared in $(properties)
    {
        result += <install-dependencies>on <install-type>SHARED_LIB <install-type>MANIFEST ;
    }
    return $(result) ;
}

rule install-vendor-api-dependencies ( properties * )
{
    local location = [ install-location $(properties) ] ;
    local dependencies ;
    if <toolset>msvc in $(properties) && <link>static in $(properties)
    {
        if [ path.exists pwiz_aux/msrc/utility/vendor_api/ABI ] { dependencies += <dependency>pwiz_aux/msrc/utility/vendor_api/ABI//install_pwiz_vendor_api_abi/$(location) ; }
        if [ path.exists pwiz_aux/msrc/utility/vendor_api/ABI/T2D ] { dependencies += <dependency>pwiz_aux/msrc/utility/vendor_api/ABI/T2D//install_pwiz_vendor_api_abi_t2d/$(location) ; }
        if [ path.exists pwiz_aux/msrc/utility/vendor_api/Agilent ] { dependencies += <dependency>pwiz_aux/msrc/utility/vendor_api/Agilent//install_pwiz_vendor_api_agilent/$(location) ; }
        if [ path.exists pwiz_aux/msrc/utility/vendor_api/Bruker ] { dependencies += <dependency>pwiz_aux/msrc/utility/vendor_api/Bruker//install_pwiz_vendor_api_bruker/$(location) ; }
        if [ path.exists pwiz_aux/msrc/utility/vendor_api/thermo ] { dependencies += <dependency>pwiz_aux/msrc/utility/vendor_api/thermo//install_pwiz_vendor_api_thermo/$(location) ; }
        if [ path.exists pwiz_aux/msrc/utility/vendor_api/Waters ] { dependencies += <dependency>pwiz_aux/msrc/utility/vendor_api/Waters//install_pwiz_vendor_api_waters/$(location) ; }
    }
    return $(dependencies) ;
}

rule seems-dependency ( properties * )
{
    local location = [ install-location $(properties) ] ;
    if <toolset>msvc in $(properties)
    {
        return <dependency>pwiz_tools/SeeMS//seems.exe/$(location) ;
    }
}

rule gcc-install-dll-path ( properties * )
{
    if <toolset>gcc in $(properties) && <link>shared in $(properties) && <target-os>linux in $(properties)
    {
        return <dll-path>'$ORIGIN' ;
    }
}


install executables
    : pwiz_tools/commandline
      pwiz_tools/sld//sldout
      pwiz_tools/examples
      #pwiz_tools/SeeMS//seems.exe
    : <conditional>@install-type
      <conditional>@install-location
      <conditional>@install-vendor-api-dependencies
      <conditional>@seems-dependency
      <conditional>@gcc-install-dll-path
      <dependency>VERSION
    ;


install bgddll
    : libraries/bgd.dll
    : <conditional>@install-location
      <toolset>gcc:<build>no
      <toolset>darwin:<build>no
    ;


install package_docs
    : doc/package/readme.txt
    : <conditional>@install-location
    ;


# convenient test target
install msconvert
    : pwiz_tools/commandline//msconvert
    : <conditional>@install-type
      <conditional>@install-location
      <conditional>@install-vendor-api-dependencies
      <conditional>@gcc-install-dll-path
    ;
explicit msconvert ;

# for copying all libraries and headers to one dir each
alias libraries : install-pwiz-lib install-boost-headers ;
explicit libraries ;

# default install location
local default-prefix = /usr/local ; # LINUX, MACOS
if [ modules.peek : NT ] { default-prefix = "C:\\" ; }
# set the default option value to be used when building the lib tarball
option.set prefix : $(default-prefix) ;

local headers = [ path.glob-tree pwiz : *.h *.hpp : .svn ] ;
package.install install-pwiz-lib
    : <install-source-root>$(PWIZ_ROOT_PATH) 
    : # executables
    : pwiz
    : $(headers) 
    ;
explicit install-pwiz-lib ;

# move boost headers separately because install-source-root is different
local boost_headers = [ path.glob-tree $(BOOST_SOURCE)/boost : *.h *.hpp : .svn ] ;
package.install install-boost-headers
    : <install-source-root>$(BOOST_SOURCE) 
    : # exe
    : # lib
    : $(boost_headers)
    ;
explicit install-boost-headers ;



# any GCC executable that uses shared libraries must have all its code built with -fPIC
rule static-with-fpic ( properties * )
{
    if ( <toolset>gcc in $(properties) || <toolset>darwin in $(properties) ) &&
       <link>static in $(properties)
    {
        return <cflags>-fPIC <linkflags>-fPIC ;
    }
}


# predicate for use as a <conditional> requirement of targets that must only build with MSVC
rule msvc-requirement ( properties * )
{
    if ! <toolset>msvc in $(properties) { return <build>no ; }
}


# TODO HACK: find location of MSFileReader DLLs (it must be in Jamroot so both vendor_readers and vendor_api can use it)

# HACK: find location of WiffFileDataReader DLLs (it must be in Jamroot so both vendor_readers and vendor_api can use it)
rule find-wiff-dlls ( api-path )
{
    local ProgramFiles = [ os.environ ProgramFiles ] ;
    ProgramFiles ?= "C:\\Program Files" ;
    local ProteinPilot = "$(ProgramFiles)\\Applied Biosystems MDS Analytical Technologies\\ProteinPilot" ;
    if [ path.exists $(api-path)\\ABSciex.DataAccess.WiffFileDataReader.dll ]
    {
        return $(api-path) ;
    }
    else if [ path.exists $(ProteinPilot)\\ABSciex.DataAccess.WiffFileDataReader.dll ]
    {
        return $(ProteinPilot) ;
    }
    else
    {
        # couldn't find Protein Pilot 3; try finding a Skyline installation under user's Local Settings

        # Windows Vista/7 have %LOCALAPPDATA%
        local LOCALAPPDATA = [ os.environ LOCALAPPDATA ] ;

        # Windows 2000/XP have %USERPROFILE%\Local Settings
        if ! $(LOCALAPPDATA) { LOCALAPPDATA = [ path.join [ os.environ USERPROFILE ] "Local Settings" ] ; }

        local skyline = [ path.glob-tree $(LOCALAPPDATA) : "rscoree.dll" ] ;

        # there could be multiple copies of Skyline installed, so we pick the first one
        if $(skyline)
        {
            return [ path.native [ path.parent $(skyline[0]) ] ] ;
        }
    }
}




# any source tree can build binary tarballs

import path ;
import common ;
import property-set ;
import sequence ;
import option ;
import property ;

.common-location = $(PWIZ_BUILD_PATH) ;

rule binary-tarball-requirements ( properties * )
{
    local toolsets = [ feature.get-values <toolset> : $(properties) ] ;
    local variants = [ feature.get-values <variant> : $(properties) ] ;
    local location = [ install-location $(properties) ] ;
    location = $(location:G=) ;

    local non-redistributables =
        *ABSciex.DataAccess.WiffFileDataReader.dll *Clear?ore*.dll *rscoree.dll         # ABI API
        *DACServer.dll *genutil.dll *raw.dll *RawCsm.dll *Security*.dll *MetaGD32.dll *MassLynxRaw.dll ; # Waters API

    if $(variants[1]) = "release"
    {
        non-redistributables += *.pdb   # MSVC debug symbols
                                *.xml ; # .NET documentation
    }

    non-redistributables = [ sequence.join $(non-redistributables) : "&&exclude:" ] ;
    local result = <tar-source>path-anchor:$(location)&&exclude:$(non-redistributables)&&$(location) ;
    if ! <architecture> in $(properties:G) { properties += <architecture>$(.platform:L) ; }
    local linkage ;
    if <link>shared in $(properties) { linkage = "-shared" ; }
    local name = [ common.format-name <base> <property:target-os> <property:architecture> <toolset> <property:variant> $(linkage) -$(version-tag:J=_)
                                      : pwiz-bin : TBZ2 : [ property-set.create $(properties) ] ] ;
    result += <name>$(name) <dependency>executables <location>$(.common-location) <dependency>VERSION ;
    return $(result) ;
}

tar.create pwiz-bin.tar.bz2
  : # sources are handled by the conditional

  : # requirements
    <conditional>@binary-tarball-requirements
  ;

rule library-tarball-requirements ( properties * )
{
    local toolsets = [ feature.get-values <toolset> : $(properties) ] ;
    local variants = [ feature.get-values <variant> : $(properties) ] ;
    
    # require that the location come from --prefix or default; do not allow --libdir or --includedir
    if [ MATCH --libdir=(.*) : [ modules.peek : ARGV ] ] ||
       [ MATCH --includedir=(.*) : [ modules.peek : ARGV ] ]
    {
        echo "--libdir and --includedir are not supported for pwiz-lib.tar.bz2; use --prefix instead" ;
        exit ;
    }

    local prefix = [ option.get prefix ] ;
    local location = $(prefix:G=) ;

    local non-redistributables =
        *ABSciex.DataAccess.WiffFileDataReader.dll *Clear?ore*.dll *rscoree.dll         # ABI API
        *DACServer.dll *genutil.dll *raw.dll *RawCsm.dll *Security*.dll *MetaGD32.dll *MassLynxRaw.dll ; # Waters API

    non-redistributables = [ sequence.join $(non-redistributables) : "&&exclude:" ] ;
    local result = <tar-source>path-anchor:$(location)&&exclude:$(non-redistributables)&&$(location) ;
    if ! <architecture> in $(properties:G) { properties += <architecture>$(.platform:L) ; }
    local linkage ;
    if <link>shared in $(properties) { linkage = "-shared" ; }
    local name = [ common.format-name <base> <property:target-os> <property:architecture> <toolset> <property:variant> $(linkage) -$(version-tag:J=_)
                                      : pwiz-lib : TBZ2 : [ property-set.create $(properties) ] ] ;
    result += <name>$(name) <dependency>libraries <location>$(.common-location) <dependency>VERSION ;
    return $(result) ;
}

tar.create pwiz-lib.tar.bz2
  : # sources are handled by the conditional

  : # requirements
    <conditional>@library-tarball-requirements
  ;
explicit pwiz-lib.tar.bz2 ;

# full source trees automatically build source tarballs

if ! [ path.exists $(PWIZ_ROOT_PATH)/SUBSET ]
{

import bcp ;
using bcp : $(BOOST_SOURCE) : $(.common-location)/bcp ;

path-constant BOOST_SUBSET_PATH : $(.common-location)/boost-subset ;

bcp.copy-boost-dependencies copy_boost_subset
  : # sources

    # scan all source files for boost dependencies
    [ path.glob-tree $(PWIZ_ROOT_PATH)/pwiz : *.cpp *.hpp ]
    [ path.glob-tree $(PWIZ_ROOT_PATH)/pwiz_aux : *.cpp *.hpp ]
    [ path.glob-tree $(PWIZ_ROOT_PATH)/pwiz_tools : *.cpp *.hpp ]
    [ path.glob-tree $(PWIZ_LIBRARIES_PATH)/boost_aux : *.cpp *.hpp ]

    # scan all targets and their dependencies
    $(PWIZ_ROOT_PATH)/pwiz_tools/commandline//mstools

  : # requirements
    <location>$(BOOST_SUBSET_PATH)/boost_$(BOOST_VERSION:J=_)
  ;


tar.create boost_$(BOOST_VERSION:J=_).tar.bz2
  : # sources

      path-anchor:$(BOOST_SUBSET_PATH)
      $(BOOST_SUBSET_PATH)/boost_$(BOOST_VERSION:J=_)

  : # requirements
    <location>$(BOOST_SUBSET_PATH)/libraries
    <dependency>copy_boost_subset
  ;

.exclusion-list =
    exclude:.svn
    exclude:*.bak
    exclude:cvgen*.exe
    exclude:svnrev.hpp
    exclude:Version.cpp
    exclude:Reader_*_Test.data
    exclude:example_data/small* # large files in example_data
    exclude:bootstrap exclude:bin.* # boost-build intermediate directories
    exclude:bin exclude:obj exclude:TestResults # C#.NET intermediate directories
    exclude:_ReSharper.* # ReSharper directory for C# projects
    exclude:*.ncb exclude:*.suo exclude:*.user exclude:*.p12 # More visual studio files
    exclude:*.xdc # .NET XML documentation sources
    exclude:tar.excluded # a place to put any files in these directories that shouldn't be tarballed
    exclude:pwiz_tools/Skyline
    exclude:pwiz_tools/Topograph
    exclude:pwiz_tools/Shared/Crawdad
    exclude:pwiz_tools/Shared/ProteomeDb
    exclude:pwiz_tools/Shared/ProteowizardWrapper
    exclude:pwiz_tools/Shared/Lib/DotNetZip-v1.8
    exclude:pwiz_tools/Shared/Lib/NHibernate
    exclude:pwiz_tools/Shared/Lib/npgsql
    exclude:pwiz_tools/Shared/Lib/System.Data.*
    exclude:pwiz_tools/Shared/Lib/MathNet.*
    exclude:pwiz_tools/Shared/Lib/mysql.*
    exclude:pwiz_tools/Shared/Lib/zlib.*
;

# the SUBSET file acts as flag so that building a subset tarball doesn't build a source tarball
make SUBSET : : @make_SUBSET : <location>$(.common-location) ;
actions make_SUBSET
{
@($(STDOUT):E=
This source tree is a subset of the full pwiz source tree.
) > "$(<)"
}

.common-sources =
    path-anchor:$(PWIZ_ROOT_PATH)
    $(.exclusion-list)
    
    $(PWIZ_ROOT_PATH)/Jamroot.jam
    $(PWIZ_ROOT_PATH)/NOTICE
    $(PWIZ_ROOT_PATH)/LICENSE
    $(PWIZ_ROOT_PATH)/quickbuild.bat
    $(PWIZ_ROOT_PATH)/quickbuild.sh
    $(PWIZ_ROOT_PATH)/clean.bat
    $(PWIZ_ROOT_PATH)/clean.sh

    $(PWIZ_ROOT_PATH)/doc
    $(PWIZ_ROOT_PATH)/example_data

    $(PWIZ_ROOT_PATH)/pwiz
    $(PWIZ_ROOT_PATH)/pwiz_aux
    $(PWIZ_ROOT_PATH)/pwiz_tools
    $(PWIZ_ROOT_PATH)/scripts

    path-anchor:$(PWIZ_BUILD_PATH)
    $(PWIZ_BUILD_PATH)/SUBSET

    path-anchor:$(PWIZ_ROOT_PATH)
;

.l = 
    $(PWIZ_LIBRARIES_PATH)/boost-build
    $(PWIZ_LIBRARIES_PATH)/boost_aux
    $(PWIZ_LIBRARIES_PATH)/libsvm-3.0
    [ path.glob $(PWIZ_LIBRARIES_PATH) : *.bat *.sh *.jam *.dll *.lib *.exe *.cpp gd*.tar.bz2 zlib*.tar.bz2 ]

    # include the bcp'd boost tarball as if it was really located at "libraries/boost_*.tar.bz2"
    path-anchor:$(BOOST_SUBSET_PATH)
    $(BOOST_SUBSET_PATH)/libraries

    path-anchor:$(PWIZ_ROOT_PATH)
;

.no-t = 
    exclude:*Test*.data*
    exclude:*Test.?pp
    exclude:*TestData.?pp
    exclude:*.pdf # pwiz posters in /doc
    exclude:example_data*
;

.no-v =
    exclude:vendor_readers/*/*.bat
    exclude:vendor_api
;

.common-requirements = <location>$(.common-location) <dependency>boost_$(BOOST_VERSION:J=_).tar.bz2 <dependency>SUBSET <dependency>VERSION ;

# l = without libraries, t = without tests, v = without vendor APIs
tar.create pwiz-src.tar.bz2             : $(.common-sources) $(.l) : $(.common-requirements) <name>pwiz-src-$(version-tag:J=_).tar.bz2 ;
tar.create pwiz-src-without-l.tar.bz2   : $(.common-sources) : $(.common-requirements) <name>pwiz-src-without-l-$(version-tag:J=_).tar.bz2 ;
tar.create pwiz-src-without-lt.tar.bz2  : $(.no-t) $(.common-sources)  : $(.common-requirements) <name>pwiz-src-without-lt-$(version-tag:J=_).tar.bz2 ;
tar.create pwiz-src-without-ltv.tar.bz2 : $(.no-t) $(.no-v) $(.common-sources) : $(.common-requirements) <name>pwiz-src-without-ltv-$(version-tag:J=_).tar.bz2 ;
tar.create pwiz-src-without-lv.tar.bz2  : $(.no-v) $(.common-sources) : $(.common-requirements) <name>pwiz-src-without-lv-$(version-tag:J=_).tar.bz2 ;
tar.create pwiz-src-without-t.tar.bz2   : $(.no-t) $(.common-sources) $(.l) : $(.common-requirements) <name>pwiz-src-without-t-$(version-tag:J=_).tar.bz2 ;
tar.create pwiz-src-without-tv.tar.bz2  : $(.no-t) $(.no-v) $(.common-sources) $(.l) : $(.common-requirements) <name>pwiz-src-without-tv-$(version-tag:J=_).tar.bz2 ;
tar.create pwiz-src-without-v.tar.bz2   : $(.no-v) $(.common-sources) $(.l) : $(.common-requirements) <name>pwiz-src-without-v-$(version-tag:J=_).tar.bz2 ;

} # if SUBSET