early-access version 3088

This commit is contained in:
pineappleEA
2022-11-05 15:35:56 +01:00
parent 4e4fc25ce3
commit b601909c6d
35519 changed files with 5996896 additions and 860 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,29 @@
# Copyright 2018 Steven Watanabe
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
import python ;
import testing ;
if ! [ python.configured ]
{
using python ;
}
# Not quite perfect, but good enough for most purposes
local test-files = [ glob *.py ] ;
local boost-build-files = [ glob
../src/tools/*.jam
../src/tools/*/*.jam
../src/build/*.jam
../src/util/*.jam
../src/kernel/*.jam
../src/options/*.jam
../src/*.jam ] ;
testing.make-test run-pyd : test_all.py :
<dependency>$(test-files)
<dependency>$(boost-build-files)
;

View File

@@ -0,0 +1,267 @@
#!/usr/bin/python
# Copyright (C) 2013 Steven Watanabe
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
import sys
def create(t):
t.write('''mockinfo.py''', '''
from __future__ import print_function
import re
import optparse
import os
parser = optparse.OptionParser()
parser.add_option('-o', dest="output_file")
parser.add_option('-x', dest="language")
parser.add_option('-c', dest="compile", action="store_true")
parser.add_option('-I', dest="includes", action="append")
parser.add_option('-D', dest="defines", action="append")
parser.add_option('-L', dest="library_path", action="append")
parser.add_option('--dll', dest="dll", action="store_true")
parser.add_option('--archive', dest="archive", action="store_true")
parser.add_option('--static-lib', dest="static_libraries", action="append")
parser.add_option('--shared-lib', dest="shared_libraries", action="append")
cwd = os.environ["JAM_CWD"]
class MockInfo(object):
def __init__(self, verbose=False):
self.files = dict()
self.commands = list()
self.verbose = verbose
def source_file(self, name, pattern):
self.files[name] = pattern
def action(self, command, status=0):
if isinstance(command, str):
command = command.split()
self.commands.append((command, status))
def check(self, command):
print("Testing command", command)
for (raw, status) in self.commands:
if self.matches(raw, command):
return status
def matches(self, raw, command):
(expected_options, expected_args) = parser.parse_args(raw)
options = command[0]
input_files = list(command[1])
if self.verbose:
print(" - matching against", (expected_options, expected_args))
if len(expected_args) != len(input_files):
if self.verbose:
print(" argument list sizes differ")
return False
for arg in expected_args:
if arg.startswith('$'):
fileid = arg[1:]
pattern = self.files[fileid] if fileid in self.files else fileid
matching_file = None
for input_file in input_files:
with open(input_file, 'r') as f:
contents = f.read()
if pattern == contents:
matching_file = input_file
break
if matching_file is not None:
input_files.remove(matching_file)
else:
if self.verbose:
print(" Failed to match input file contents: %s" % arg)
return False
else:
if arg in input_files:
input_files.remove(arg)
else:
if self.verbose:
print(" Failed to match input file: %s" % arg)
return False
if options.language != expected_options.language:
if self.verbose:
print(" Failed to match -c")
return False
if options.compile != expected_options.compile:
if self.verbose:
print(" Failed to match -x")
return False
# Normalize a path for comparison purposes
def adjust_path(p):
return os.path.normcase(os.path.normpath(os.path.join(cwd, p)))
# order matters
if options.includes is None:
options.includes = []
if expected_options.includes is None:
expected_options.includes = []
if list(map(adjust_path, options.includes)) != \
list(map(adjust_path, expected_options.includes)):
if self.verbose:
print(" Failed to match -I ", list(map(adjust_path, options.includes)), \
" != ", list(map(adjust_path, expected_options.includes)))
return False
if options.defines is None:
options.defines = []
if expected_options.defines is None:
expected_options.defines = []
if options.defines != expected_options.defines:
if self.verbose:
print(" Failed to match -I ", options.defines, \
" != ", expected_options.defines)
return False
if options.library_path is None:
options.library_path = []
if expected_options.library_path is None:
expected_options.library_path = []
if list(map(adjust_path, options.library_path)) != \
list(map(adjust_path, expected_options.library_path)):
if self.verbose:
print(" Failed to match -L ", list(map(adjust_path, options.library_path)), \
" != ", list(map(adjust_path, expected_options.library_path)))
return False
if options.static_libraries != expected_options.static_libraries:
if self.verbose:
print(" Failed to match --static-lib")
return False
if options.shared_libraries != expected_options.shared_libraries:
if self.verbose:
print(" Failed to match --shared-lib")
return False
if options.dll != expected_options.dll:
if self.verbose:
print(" Failed to match --dll")
return False
if options.archive != expected_options.archive:
if self.verbose:
print(" Failed to match --archive")
return False
# The output must be handled after everything else
# is validated
if expected_options.output_file is not None:
if options.output_file is not None:
if expected_options.output_file.startswith('$'):
fileid = expected_options.output_file[1:]
if fileid not in self.files:
self.files[fileid] = fileid
else:
assert(self.files[fileid] == fileid)
with open(options.output_file, 'w') as output:
output.write(fileid)
else:
if self.verbose:
print("Failed to match -o")
return False
elif options.output_file is not None:
if self.verbose:
print("Failed to match -o")
return False
# if we've gotten here, then everything matched
if self.verbose:
print(" Matched")
return True
''')
t.write('mock.py', '''
from __future__ import print_function
import mockinfo
import markup
import sys
status = markup.info.check(mockinfo.parser.parse_args())
if status is not None:
exit(status)
else:
print("Unrecognized command: " + ' '.join(sys.argv))
exit(1)
''')
t.write('mock.jam', '''
import feature ;
import toolset ;
import path ;
import modules ;
import common ;
import type ;
.python-cmd = "\"%s\"" ;
# Behave the same as gcc on Windows, because that's what
# the test system expects
type.set-generated-target-prefix SHARED_LIB : <toolset>mock <target-os>windows : lib ;
type.set-generated-target-suffix STATIC_LIB : <toolset>mock <target-os>windows : a ;
rule init ( )
{
local here = [ path.make [ modules.binding $(__name__) ] ] ;
here = [ path.native [ path.root [ path.parent $(here) ] [ path.pwd ] ] ] ;
.config-cmd = [ common.variable-setting-command JAM_CWD : $(here) ] $(.python-cmd) -B ;
}
feature.extend toolset : mock ;
generators.register-c-compiler mock.compile.c++ : CPP : OBJ : <toolset>mock ;
generators.register-c-compiler mock.compile.c : C : OBJ : <toolset>mock ;
generators.register-linker mock.link : LIB OBJ : EXE : <toolset>mock ;
generators.register-linker mock.link.dll : LIB OBJ : SHARED_LIB : <toolset>mock ;
generators.register-archiver mock.archive : OBJ : STATIC_LIB : <toolset>mock ;
toolset.flags mock.compile OPTIONS <link>shared : -fPIC ;
toolset.flags mock.compile INCLUDES : <include> ;
toolset.flags mock.compile DEFINES : <define> ;
actions compile.c
{
$(.config-cmd) mock.py -c -x c -I"$(INCLUDES)" -D"$(DEFINES)" "$(>)" -o "$(<)"
}
actions compile.c++
{
$(.config-cmd) mock.py -c -x c++ -I"$(INCLUDES)" -D"$(DEFINES)" "$(>)" -o "$(<)"
}
toolset.flags mock.link USER_OPTIONS <linkflags> ;
toolset.flags mock.link FINDLIBS-STATIC <find-static-library> ;
toolset.flags mock.link FINDLIBS-SHARED <find-shared-library> ;
toolset.flags mock.link LINK_PATH <library-path> ;
toolset.flags mock.link LIBRARIES <library-file> ;
actions link
{
$(.config-cmd) mock.py "$(>)" -o "$(<)" $(USER_OPTIONS) -L"$(LINK_PATH)" --static-lib=$(FINDLIBS-STATIC) --shared-lib=$(FINDLIBS-SHARED)
}
actions archive
{
$(.config-cmd) mock.py --archive "$(>)" -o "$(<)" $(USER_OPTIONS)
}
actions link.dll
{
$(.config-cmd) mock.py --dll "$(>)" -o "$(<)" $(USER_OPTIONS) -L"$(LINK_PATH)" --static-lib=$(FINDLIBS-STATIC) --shared-lib=$(FINDLIBS-SHARED)
}
''' % sys.executable.replace('\\', '\\\\'))
def set_expected(t, markup):
verbose = "True" if t.verbose else "False"
t.write('markup.py', '''
import mockinfo
info = mockinfo.MockInfo(%s)
def source_file(name, contents):
info.source_file(name, contents)
def action(command, status=0):
info.action(command, status)
''' % (verbose) + markup)

View File

@@ -0,0 +1,609 @@
"""
TestCmd.py: a testing framework for commands and scripts.
The TestCmd module provides a framework for portable automated testing of
executable commands and scripts (in any language, not just Python), especially
commands and scripts that require file system interaction.
In addition to running tests and evaluating conditions, the TestCmd module
manages and cleans up one or more temporary workspace directories, and provides
methods for creating files and directories in those workspace directories from
in-line data, here-documents), allowing tests to be completely self-contained.
A TestCmd environment object is created via the usual invocation:
test = TestCmd()
The TestCmd module provides pass_test(), fail_test(), and no_result() unbound
methods that report test results for use with the Aegis change management
system. These methods terminate the test immediately, reporting PASSED, FAILED
or NO RESULT respectively and exiting with status 0 (success), 1 or 2
respectively. This allows for a distinction between an actual failed test and a
test that could not be properly evaluated because of an external condition (such
as a full file system or incorrect permissions).
"""
# Copyright 2000 Steven Knight
# This module is free software, and you may redistribute it and/or modify
# it under the same terms as Python itself, so long as this copyright message
# and disclaimer are retained in their original form.
#
# IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
# SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF
# THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
# DAMAGE.
#
# THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
# PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS,
# AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
# Copyright 2002-2003 Vladimir Prus.
# Copyright 2002-2003 Dave Abrahams.
# Copyright 2006 Rene Rivera.
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
from __future__ import print_function
__author__ = "Steven Knight <knight@baldmt.com>"
__revision__ = "TestCmd.py 0.D002 2001/08/31 14:56:12 software"
__version__ = "0.02"
from types import *
import os
import os.path
import re
import shutil
import stat
import subprocess
import sys
import tempfile
import traceback
tempfile.template = 'testcmd.'
_Cleanup = []
def _clean():
global _Cleanup
list = _Cleanup[:]
_Cleanup = []
list.reverse()
for test in list:
test.cleanup()
sys.exitfunc = _clean
def caller(tblist, skip):
string = ""
arr = []
for file, line, name, text in tblist:
if file[-10:] == "TestCmd.py":
break
arr = [(file, line, name, text)] + arr
atfrom = "at"
for file, line, name, text in arr[skip:]:
if name == "?":
name = ""
else:
name = " (" + name + ")"
string = string + ("%s line %d of %s%s\n" % (atfrom, line, file, name))
atfrom = "\tfrom"
return string
def fail_test(self=None, condition=True, function=None, skip=0):
"""Cause the test to fail.
By default, the fail_test() method reports that the test FAILED and exits
with a status of 1. If a condition argument is supplied, the test fails
only if the condition is true.
"""
if not condition:
return
if not function is None:
function()
of = ""
desc = ""
sep = " "
if not self is None:
if self.program:
of = " of " + " ".join(self.program)
sep = "\n\t"
if self.description:
desc = " [" + self.description + "]"
sep = "\n\t"
at = caller(traceback.extract_stack(), skip)
sys.stderr.write("FAILED test" + of + desc + sep + at + """
in directory: """ + os.getcwd() )
sys.exit(1)
def no_result(self=None, condition=True, function=None, skip=0):
"""Causes a test to exit with no valid result.
By default, the no_result() method reports NO RESULT for the test and
exits with a status of 2. If a condition argument is supplied, the test
fails only if the condition is true.
"""
if not condition:
return
if not function is None:
function()
of = ""
desc = ""
sep = " "
if not self is None:
if self.program:
of = " of " + self.program
sep = "\n\t"
if self.description:
desc = " [" + self.description + "]"
sep = "\n\t"
at = caller(traceback.extract_stack(), skip)
sys.stderr.write("NO RESULT for test" + of + desc + sep + at)
sys.exit(2)
def pass_test(self=None, condition=True, function=None):
"""Causes a test to pass.
By default, the pass_test() method reports PASSED for the test and exits
with a status of 0. If a condition argument is supplied, the test passes
only if the condition is true.
"""
if not condition:
return
if not function is None:
function()
sys.stderr.write("PASSED\n")
sys.exit(0)
class MatchError(object):
def __init__(self, message):
self.message = message
def __nonzero__(self):
return False
def __bool__(self):
return False
def match_exact(lines=None, matches=None):
"""
Returns whether the given lists or strings containing lines separated
using newline characters contain exactly the same data.
"""
if not type(lines) is list:
lines = lines.split("\n")
if not type(matches) is list:
matches = matches.split("\n")
if len(lines) != len(matches):
return
for i in range(len(lines)):
if lines[i] != matches[i]:
return MatchError("Mismatch at line %d\n- %s\n+ %s\n" %
(i+1, matches[i], lines[i]))
if len(lines) < len(matches):
return MatchError("Missing lines at line %d\n- %s" %
(len(lines), "\n- ".join(matches[len(lines):])))
if len(lines) > len(matches):
return MatchError("Extra lines at line %d\n+ %s" %
(len(matches), "\n+ ".join(lines[len(matches):])))
return 1
def match_re(lines=None, res=None):
"""
Given lists or strings contain lines separated using newline characters.
This function matches those lines one by one, interpreting the lines in the
res parameter as regular expressions.
"""
if not type(lines) is list:
lines = lines.split("\n")
if not type(res) is list:
res = res.split("\n")
for i in range(min(len(lines), len(res))):
if not re.compile("^" + res[i] + "$").search(lines[i]):
return MatchError("Mismatch at line %d\n- %s\n+ %s\n" %
(i+1, res[i], lines[i]))
if len(lines) < len(res):
return MatchError("Missing lines at line %d\n- %s" %
(len(lines), "\n- ".join(res[len(lines):])))
if len(lines) > len(res):
return MatchError("Extra lines at line %d\n+ %s" %
(len(res), "\n+ ".join(lines[len(res):])))
return 1
class TestCmd:
def __init__(self, description=None, program=None, workdir=None,
subdir=None, verbose=False, match=None, inpath=None):
self._cwd = os.getcwd()
self.description_set(description)
self.program_set(program, inpath)
self.verbose_set(verbose)
if match is None:
self.match_func = match_re
else:
self.match_func = match
self._dirlist = []
self._preserve = {'pass_test': 0, 'fail_test': 0, 'no_result': 0}
env = os.environ.get('PRESERVE')
if env:
self._preserve['pass_test'] = env
self._preserve['fail_test'] = env
self._preserve['no_result'] = env
else:
env = os.environ.get('PRESERVE_PASS')
if env is not None:
self._preserve['pass_test'] = env
env = os.environ.get('PRESERVE_FAIL')
if env is not None:
self._preserve['fail_test'] = env
env = os.environ.get('PRESERVE_PASS')
if env is not None:
self._preserve['PRESERVE_NO_RESULT'] = env
self._stdout = []
self._stderr = []
self.status = None
self.condition = 'no_result'
self.workdir_set(workdir)
self.subdir(subdir)
def __del__(self):
self.cleanup()
def __repr__(self):
return "%x" % id(self)
def cleanup(self, condition=None):
"""
Removes any temporary working directories for the specified TestCmd
environment. If the environment variable PRESERVE was set when the
TestCmd environment was created, temporary working directories are not
removed. If any of the environment variables PRESERVE_PASS,
PRESERVE_FAIL or PRESERVE_NO_RESULT were set when the TestCmd
environment was created, then temporary working directories are not
removed if the test passed, failed or had no result, respectively.
Temporary working directories are also preserved for conditions
specified via the preserve method.
Typically, this method is not called directly, but is used when the
script exits to clean up temporary working directories as appropriate
for the exit status.
"""
if not self._dirlist:
return
if condition is None:
condition = self.condition
if self._preserve[condition]:
for dir in self._dirlist:
print("Preserved directory %s" % dir)
else:
list = self._dirlist[:]
list.reverse()
for dir in list:
self.writable(dir, 1)
shutil.rmtree(dir, ignore_errors=1)
self._dirlist = []
self.workdir = None
os.chdir(self._cwd)
try:
global _Cleanup
_Cleanup.remove(self)
except (AttributeError, ValueError):
pass
def description_set(self, description):
"""Set the description of the functionality being tested."""
self.description = description
def fail_test(self, condition=True, function=None, skip=0):
"""Cause the test to fail."""
if not condition:
return
self.condition = 'fail_test'
fail_test(self = self,
condition = condition,
function = function,
skip = skip)
def match(self, lines, matches):
"""Compare actual and expected file contents."""
return self.match_func(lines, matches)
def match_exact(self, lines, matches):
"""Compare actual and expected file content exactly."""
return match_exact(lines, matches)
def match_re(self, lines, res):
"""Compare file content with a regular expression."""
return match_re(lines, res)
def no_result(self, condition=True, function=None, skip=0):
"""Report that the test could not be run."""
if not condition:
return
self.condition = 'no_result'
no_result(self = self,
condition = condition,
function = function,
skip = skip)
def pass_test(self, condition=True, function=None):
"""Cause the test to pass."""
if not condition:
return
self.condition = 'pass_test'
pass_test(self, condition, function)
def preserve(self, *conditions):
"""
Arrange for the temporary working directories for the specified
TestCmd environment to be preserved for one or more conditions. If no
conditions are specified, arranges for the temporary working
directories to be preserved for all conditions.
"""
if conditions == ():
conditions = ('pass_test', 'fail_test', 'no_result')
for cond in conditions:
self._preserve[cond] = 1
def program_set(self, program, inpath):
"""Set the executable program or script to be tested."""
if not inpath and program and not os.path.isabs(program[0]):
program[0] = os.path.join(self._cwd, program[0])
self.program = program
def read(self, file, mode='rb'):
"""
Reads and returns the contents of the specified file name. The file
name may be a list, in which case the elements are concatenated with
the os.path.join() method. The file is assumed to be under the
temporary working directory unless it is an absolute path name. The I/O
mode for the file may be specified and must begin with an 'r'. The
default is 'rb' (binary read).
"""
if type(file) is list:
file = os.path.join(*file)
if not os.path.isabs(file):
file = os.path.join(self.workdir, file)
if mode[0] != 'r':
raise ValueError("mode must begin with 'r'")
return open(file, mode).read()
def run(self, program=None, arguments=None, chdir=None, stdin=None,
universal_newlines=True):
"""
Runs a test of the program or script for the test environment.
Standard output and error output are saved for future retrieval via the
stdout() and stderr() methods.
'universal_newlines' parameter controls how the child process
input/output streams are opened as defined for the same named Python
subprocess.POpen constructor parameter.
"""
if chdir:
if not os.path.isabs(chdir):
chdir = os.path.join(self.workpath(chdir))
if self.verbose:
sys.stderr.write("chdir(" + chdir + ")\n")
else:
chdir = self.workdir
cmd = []
if program and program[0]:
if program[0] != self.program[0] and not os.path.isabs(program[0]):
program[0] = os.path.join(self._cwd, program[0])
cmd += program
else:
cmd += self.program
if arguments:
cmd += arguments.split(" ")
if self.verbose:
sys.stderr.write("run(" + " ".join(cmd) + ")\n")
p = subprocess.Popen(cmd, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=chdir,
universal_newlines=universal_newlines)
if stdin:
if type(stdin) is list:
stdin = "".join(stdin)
out, err = p.communicate(stdin)
if not type(out) is str:
out = out.decode()
if not type(err) is str:
err = err.decode()
self._stdout.append(out)
self._stderr.append(err)
self.status = p.returncode
if self.verbose:
sys.stdout.write(self._stdout[-1])
sys.stderr.write(self._stderr[-1])
def stderr(self, run=None):
"""
Returns the error output from the specified run number. If there is
no specified run number, then returns the error output of the last run.
If the run number is less than zero, then returns the error output from
that many runs back from the current run.
"""
if not run:
run = len(self._stderr)
elif run < 0:
run = len(self._stderr) + run
run -= 1
if run < 0:
return ''
return self._stderr[run]
def stdout(self, run=None):
"""
Returns the standard output from the specified run number. If there
is no specified run number, then returns the standard output of the
last run. If the run number is less than zero, then returns the
standard output from that many runs back from the current run.
"""
if not run:
run = len(self._stdout)
elif run < 0:
run = len(self._stdout) + run
run -= 1
if run < 0:
return ''
return self._stdout[run]
def subdir(self, *subdirs):
"""
Create new subdirectories under the temporary working directory, one
for each argument. An argument may be a list, in which case the list
elements are concatenated using the os.path.join() method.
Subdirectories multiple levels deep must be created using a separate
argument for each level:
test.subdir('sub', ['sub', 'dir'], ['sub', 'dir', 'ectory'])
Returns the number of subdirectories actually created.
"""
count = 0
for sub in subdirs:
if sub is None:
continue
if type(sub) is list:
sub = os.path.join(*tuple(sub))
new = os.path.join(self.workdir, sub)
try:
os.mkdir(new)
except:
pass
else:
count += 1
return count
def unlink(self, file):
"""
Unlinks the specified file name. The file name may be a list, in
which case the elements are concatenated using the os.path.join()
method. The file is assumed to be under the temporary working directory
unless it is an absolute path name.
"""
if type(file) is list:
file = os.path.join(*tuple(file))
if not os.path.isabs(file):
file = os.path.join(self.workdir, file)
os.unlink(file)
def verbose_set(self, verbose):
"""Set the verbose level."""
self.verbose = verbose
def workdir_set(self, path):
"""
Creates a temporary working directory with the specified path name.
If the path is a null string (''), a unique directory name is created.
"""
if os.path.isabs(path):
self.workdir = path
else:
if path != None:
if path == '':
path = tempfile.mktemp()
if path != None:
os.mkdir(path)
self._dirlist.append(path)
global _Cleanup
try:
_Cleanup.index(self)
except ValueError:
_Cleanup.append(self)
# We would like to set self.workdir like this:
# self.workdir = path
# But symlinks in the path will report things differently from
# os.getcwd(), so chdir there and back to fetch the canonical
# path.
cwd = os.getcwd()
os.chdir(path)
self.workdir = os.getcwd()
os.chdir(cwd)
else:
self.workdir = None
def workpath(self, *args):
"""
Returns the absolute path name to a subdirectory or file within the
current temporary working directory. Concatenates the temporary working
directory name with the specified arguments using os.path.join().
"""
return os.path.join(self.workdir, *tuple(args))
def writable(self, top, write):
"""
Make the specified directory tree writable (write == 1) or not
(write == None).
"""
def _walk_chmod(arg, dirname, names):
st = os.stat(dirname)
os.chmod(dirname, arg(st[stat.ST_MODE]))
for name in names:
fullname = os.path.join(dirname, name)
st = os.stat(fullname)
os.chmod(fullname, arg(st[stat.ST_MODE]))
_mode_writable = lambda mode: stat.S_IMODE(mode|0o200)
_mode_non_writable = lambda mode: stat.S_IMODE(mode&~0o200)
if write:
f = _mode_writable
else:
f = _mode_non_writable
try:
for root, _, files in os.walk(top):
_walk_chmod(f, root, files)
except:
pass # Ignore any problems changing modes.
def write(self, file, content, mode='wb'):
"""
Writes the specified content text (second argument) to the specified
file name (first argument). The file name may be a list, in which case
the elements are concatenated using the os.path.join() method. The file
is created under the temporary working directory. Any subdirectories in
the path must already exist. The I/O mode for the file may be specified
and must begin with a 'w'. The default is 'wb' (binary write).
"""
if type(file) is list:
file = os.path.join(*tuple(file))
if not os.path.isabs(file):
file = os.path.join(self.workdir, file)
if mode[0] != 'w':
raise ValueError("mode must begin with 'w'")
open(file, mode).write(content)

View File

@@ -0,0 +1,123 @@
#!/usr/bin/python
#
# Copyright 2017 Steven Watanabe
#
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
# validates a toolset using a mock of the compiler
import BoostBuild
import os
import re
import sys
renames = {"debug": "variant=debug", "release": "variant=release"}
def set_default_target_os(os):
global removed
global default_target_os
default_target_os = os
removed = set()
removed.add("target-os=" + default_target_os)
def adjust_property(property):
global renames
if property in renames:
return renames[property]
else:
return property
def adjust_properties(properties):
global removed
return [adjust_property(p) for p in properties if p not in removed]
def has_property(name, properties):
return name in [re.sub("=.*", "", p) for p in properties]
def get_property(name, properties):
for m in [re.match("(.*)=(.*)", p) for p in properties]:
if m and m.group(1) == name:
return m.group(2)
def get_target_os(properties):
return get_property("target-os", properties) or default_target_os
def expand_properties(properties):
result = properties[:]
if not has_property("variant", properties):
result += ["variant=debug"]
if not has_property("threading", properties):
result += ["threading=single"]
if not has_property("exception-handling", properties):
result += ["exception-handling=on"]
if not has_property("link", properties):
result += ["link=shared"]
if not has_property("rtti", properties):
result += ["rtti=on"]
if not has_property("runtime-link", properties):
result += ["runtime-link=shared"]
if not has_property("strip", properties):
result += ["strip=off"]
if not has_property("target-os", properties):
result += ["target-os=" + default_target_os]
return result
def compute_path(properties, target_type):
path = ""
if "variant=release" in properties:
path += "/release"
else:
path += "/debug"
if has_property("address-model", properties):
path += "/address-model-" + get_property("address-model", properties)
if has_property("architecture", properties):
path += "/architecture-" + get_property("architecture", properties)
if "cxxstd=latest" in properties:
path += "/cxxstd-latest-iso"
if "exception-handling=off" in properties:
path += "/exception-handling-off"
if "link=static" in properties:
path += "/link-static"
if "rtti=off" in properties:
path += "/rtti-off"
if "runtime-link=static" in properties and target_type in ["exe"]:
path += "/runtime-link-static"
if "strip=on" in properties and target_type in ["dll", "exe", "obj2"]:
path += "/strip-on"
if get_target_os(properties) != default_target_os:
path += "/target-os-" + get_target_os(properties)
if "threading=multi" in properties:
path += "/threading-multi"
return path
def test_toolset(toolset, version, property_sets):
t = BoostBuild.Tester()
t.set_tree("toolset-mock")
# Build necessary tools
t.run_build_system(["-sPYTHON_CMD=%s" % sys.executable], subdir="src")
set_default_target_os(t.read("src/bin/target-os.txt").strip())
for properties in property_sets:
t.set_toolset(toolset + "-" + version, get_target_os(properties))
properties = adjust_properties(properties)
def path(t):
return toolset.split("-")[0] + "-*" + version + compute_path(properties, t)
os.environ["B2_PROPERTIES"] = " ".join(expand_properties(properties))
t.run_build_system(["--user-config=", "-sPYTHON_CMD=%s" % sys.executable] + properties)
t.expect_addition("bin/%s/lib.obj" % (path("obj")))
if "link=static" not in properties:
t.expect_addition("bin/%s/l1.dll" % (path("dll")))
t.ignore_addition("bin/%s/*l1.*.rsp" % (path("dll")))
else:
t.expect_addition("bin/%s/l1.lib" % (path("lib")))
t.expect_addition("bin/%s/main.obj" % (path("obj2")))
t.expect_addition("bin/%s/test.exe" % (path("exe")))
t.ignore_addition("bin/%s/test.rsp" % (path("exe")))
t.expect_nothing_more()
t.rm("bin")
t.cleanup()

View File

@@ -0,0 +1,39 @@
# Niklaus Giger, 2005-03-15
# Testing whether we may run a test in absolute directories. There are no tests
# for temporary directories as this is implictly tested in a lot of other cases.
# TODO: Move to a separate testing-system test group.
import BoostBuild
import os
import tempfile
# Python 2.7 does not implement os.path.samefile on Windows
import ntpath
if not hasattr(ntpath, "samefile"):
def samefile(f1, f2):
try:
from os.path.nt import _getfinalpathname
return os.path._getfinalpathname(f1) == os.path._getfinalpathname(f2)
except (NotImplementedError, ImportError):
return os.path.abspath(f1) == os.path.abspath(f2)
ntpath.samefile = samefile
t = BoostBuild.Tester(["-ffile.jam"], workdir=os.getcwd(), pass_d0=False,
pass_toolset=False)
t.write("file.jam", "EXIT [ PWD ] : 0 ;")
t.run_build_system()
t.fail_test(not os.path.samefile(t.stdout().rstrip("\n"), os.getcwd()))
try:
t.run_build_system(status=123, subdir="/must/fail/with/absolute/path",
stderr=None)
except ValueError as e:
assert "subdir" in str(e), e
else:
raise ValueError("exception expected")
finally:
t.cleanup()

View File

@@ -0,0 +1,73 @@
#!/usr/bin/python
# Copyright 2003, 2004 Vladimir Prus
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
# Test that sources with absolute names are handled OK.
import BoostBuild
t = BoostBuild.Tester(use_test_config=False)
t.write("jamroot.jam", "path-constant TOP : . ;")
t.write("jamfile.jam", """\
local pwd = [ PWD ] ;
ECHO $(pwd) XXXXX ;
exe hello : $(pwd)/hello.cpp $(TOP)/empty.cpp ;
""")
t.write("hello.cpp", "int main() {}\n")
t.write("empty.cpp", "\n")
t.run_build_system()
t.expect_addition("bin/$toolset/debug*/hello.exe")
t.rm(".")
# Test a contrived case in which an absolute name is used in a standalone
# project (not Jamfile). Moreover, the target with an absolute name is returned
# via an 'alias' and used from another project.
t.write("a.cpp", "int main() {}\n")
t.write("jamfile.jam", "exe a : /standalone//a ;")
t.write("jamroot.jam", "import standalone ;")
t.write("standalone.jam", """\
import project ;
project.initialize $(__name__) ;
project standalone ;
local pwd = [ PWD ] ;
alias a : $(pwd)/a.cpp ;
""")
t.write("standalone.py", """
from b2.manager import get_manager
# FIXME: this is ugly as death
get_manager().projects().initialize(__name__)
import os ;
# This use of list as parameter is also ugly.
project(['standalone'])
pwd = os.getcwd()
alias('a', [os.path.join(pwd, 'a.cpp')])
""")
t.run_build_system()
t.expect_addition("bin/$toolset/debug*/a.exe")
# Test absolute path in target ids.
t.rm(".")
t.write("d1/jamroot.jam", "")
t.write("d1/jamfile.jam", "exe a : a.cpp ;")
t.write("d1/a.cpp", "int main() {}\n")
t.write("d2/jamroot.jam", "")
t.write("d2/jamfile.jam", """\
local pwd = [ PWD ] ;
alias x : $(pwd)/../d1//a ;
""")
t.run_build_system(subdir="d2")
t.expect_addition("d1/bin/$toolset/debug*/a.exe")
t.cleanup()

View File

@@ -0,0 +1,116 @@
#!/usr/bin/python
# Copyright 2003 Dave Abrahams
# Copyright 2003 Vladimir Prus
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
import BoostBuild
###############################################################################
#
# test_alias_rule()
# -----------------
#
###############################################################################
def test_alias_rule(t):
"""Basic alias rule test."""
t.write("jamroot.jam", """\
exe a : a.cpp ;
exe b : b.cpp ;
exe c : c.cpp ;
alias bin1 : a ;
alias bin2 : a b ;
alias src : s.cpp ;
exe hello : hello.cpp src ;
""")
t.write("a.cpp", "int main() {}\n")
t.copy("a.cpp", "b.cpp")
t.copy("a.cpp", "c.cpp")
t.copy("a.cpp", "hello.cpp")
t.write("s.cpp", "")
# Check that targets to which "bin1" refers are updated, and only those.
t.run_build_system(["bin1"])
t.expect_addition(BoostBuild.List("bin/$toolset/debug*/") * "a.exe a.obj")
t.ignore_addition('bin/*/a.rsp')
t.ignore_addition('bin/*/a.*.rsp')
t.expect_nothing_more()
# Try again with "bin2"
t.run_build_system(["bin2"])
t.expect_addition(BoostBuild.List("bin/$toolset/debug*/") * "b.exe b.obj")
t.ignore_addition('bin/*/b.rsp')
t.ignore_addition('bin/*/b.*.rsp')
t.expect_nothing_more()
# Try building everything, making sure 'hello' target is created.
t.run_build_system()
t.expect_addition(BoostBuild.List("bin/$toolset/debug*/") * \
"hello.exe hello.obj")
t.ignore_addition('bin/*/hello.rsp')
t.ignore_addition('bin/*/hello.*.rsp')
t.expect_addition("bin/$toolset/debug*/s.obj")
t.ignore_addition('bin/*/s.*.rsp')
t.expect_addition(BoostBuild.List("bin/$toolset/debug*/") * "c.exe c.obj")
t.ignore_addition('bin/*/c.rsp')
t.ignore_addition('bin/*/c.*.rsp')
t.expect_nothing_more()
###############################################################################
#
# test_alias_source_usage_requirements()
# --------------------------------------
#
###############################################################################
def test_alias_source_usage_requirements(t):
"""
Check whether usage requirements are propagated via "alias". In case they
are not, linking will fail as there will be no main() function defined
anywhere in the source.
"""
t.write("jamroot.jam", """\
lib l : l.cpp : : : <define>WANT_MAIN ;
alias la : l ;
exe main : main.cpp la ;
""")
t.write("l.cpp", """\
void
#if defined(_WIN32)
__declspec(dllexport)
#endif
foo() {}
""")
t.write("main.cpp", """\
#ifdef WANT_MAIN
int main() {}
#endif
""")
t.run_build_system()
###############################################################################
#
# main()
# ------
#
###############################################################################
t = BoostBuild.Tester(use_test_config=False)
test_alias_rule(t)
test_alias_source_usage_requirements(t)
t.cleanup()

View File

@@ -0,0 +1,129 @@
#!/usr/bin/python
# Copyright 2003 Dave Abrahams
# Copyright 2003, 2006 Vladimir Prus
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
# Test main target alternatives.
import BoostBuild
import string
t = BoostBuild.Tester(use_test_config=False)
# Test that basic alternatives selection works.
t.write("jamroot.jam", "")
t.write("jamfile.jam", """
exe a : a_empty.cpp ;
exe a : a.cpp : <variant>release ;
""")
t.write("a_empty.cpp", "")
t.write("a.cpp", "int main() {}\n")
t.run_build_system(["release"])
t.expect_addition("bin/$toolset/release*/a.exe")
# Test that alternative selection works for ordinary properties, in particular
# user-defined.
t.write("jamroot.jam", "")
t.write("jamfile.jam", """
import feature ;
feature.feature X : off on : propagated ;
exe a : b.cpp ;
exe a : a.cpp : <X>on ;
""")
t.write("b.cpp", "int main() {}\n")
t.rm("bin")
t.run_build_system()
t.expect_addition("bin/$toolset/debug*/b.obj")
t.run_build_system(["X=on"])
t.expect_addition("bin/$toolset/debug/X-on*/a.obj")
t.rm("bin")
# Test that everything works ok even with the default build.
t.write("jamfile.jam", """\
exe a : a_empty.cpp : <variant>release ;
exe a : a.cpp : <variant>debug ;
""")
t.run_build_system()
t.expect_addition("bin/$toolset/debug*/a.exe")
# Test that only properties which are in the build request matter for
# alternative selection. IOW, alternative with <variant>release is better than
# one with <variant>debug when building the release variant.
t.write("jamfile.jam", """\
exe a : a_empty.cpp : <variant>debug ;
exe a : a.cpp : <variant>release ;
""")
t.run_build_system(["release"])
t.expect_addition("bin/$toolset/release*/a.exe")
# Test that free properties do not matter. We really do not want <cxxflags>
# property in build request to affect alternative selection.
t.write("jamfile.jam", """
exe a : a_empty.cpp : <variant>debug <define>FOO <include>BAR ;
exe a : a.cpp : <variant>release ;
""")
t.rm("bin/$toolset/release/a.exe")
t.rm("bin/$toolset/release/*/a.exe")
t.run_build_system(["release", "define=FOO"])
t.expect_addition("bin/$toolset/release*/a.exe")
# Test that ambiguity is reported correctly.
t.write("jamfile.jam", """\
exe a : a_empty.cpp ;
exe a : a.cpp ;
""")
t.run_build_system(["--no-error-backtrace"], status=None)
t.expect_output_lines("error: No best alternative for ./a")
# Another ambiguity test: two matches properties in one alternative are neither
# better nor worse than a single one in another alternative.
t.write("jamfile.jam", """\
exe a : a_empty.cpp : <optimization>off <profiling>off ;
exe a : a.cpp : <debug-symbols>on ;
""")
t.run_build_system(["--no-error-backtrace"], status=None)
t.expect_output_lines("error: No best alternative for ./a")
t.rm("bin")
# Test that we can have alternative without sources.
t.write("jamfile.jam", """\
alias specific-sources ;
import feature ;
feature.extend os : MAGIC ;
alias specific-sources : b.cpp : <os>MAGIC ;
exe a : a.cpp specific-sources ;
""")
t.run_build_system()
t.expect_addition("bin/$toolset/debug*/a.exe")
t.rm("bin")
# Test that subfeatures are expanded in alternatives
# and that unknown subfeatures fail to match instead of
# causing errors.
t.write("jamfile.jam", """\
import feature : feature subfeature ;
feature X : off on : propagated ;
subfeature X on : version : 1 : propagated ;
exe a : a.cpp : <X>on-1 ;
exe a : a_empty.cpp ;
exe a : a_empty.cpp : <X>on-2 ;
""")
t.run_build_system(["X=on-1"])
t.cleanup()

View File

@@ -0,0 +1,34 @@
#!/usr/bin/python
# Copyright 2016 Steven Watanabe
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
import BoostBuild
t = BoostBuild.Tester(use_test_config=False)
t.write("main.cpp", """\
int main() {}
""")
t.write("Jamroot", """\
exe test : main.cpp ;
always test ;
""")
t.run_build_system()
t.expect_addition("bin/$toolset/debug*/main.obj")
t.ignore_addition('bin/*/main.*.rsp')
t.expect_addition("bin/$toolset/debug*/test.exe")
t.ignore_addition('bin/*/test.rsp')
t.expect_nothing_more()
t.run_build_system()
t.expect_touch("bin/$toolset/debug*/main.obj")
t.ignore_touch('bin/*/main.*.rsp')
t.expect_touch("bin/$toolset/debug*/test.exe")
t.ignore_touch('bin/*/test.rsp')
t.expect_nothing_more()
t.cleanup()

View File

@@ -0,0 +1,22 @@
#!/usr/bin/python
# Copyright 2003 Vladimir Prus
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
# Regression test: when directory of project root contained regex
# metacharacters, B2 failed to work. Bug reported by Michael Stevens.
import BoostBuild
t = BoostBuild.Tester()
t.write("bad[abc]dirname/jamfile.jam", """
""")
t.write("bad[abc]dirname/jamroot.jam", """
""")
t.run_build_system(subdir="bad[abc]dirname")
t.cleanup()

View File

@@ -0,0 +1,14 @@
# Copyright 2002, 2003 Dave Abrahams
# Copyright 2003 Vladimir Prus
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
# Assume BOOST_BUILD_PATH point to the 'test' directory.
# We need to leave 'test' there, so that 'test-config.jam'
# can be found, but also add parent directory, to find
# all the other modules.
BOOST_BUILD_PATH = $(BOOST_BUILD_PATH)/.. $(BOOST_BUILD_PATH) ;
# Find the boost build system in the ../kernel directory.
boost-build ../src/kernel ;

View File

@@ -0,0 +1,23 @@
#!/usr/bin/python
# Copyright 2004, 2006 Vladimir Prus
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
import BoostBuild
import string
t = BoostBuild.Tester()
t.set_tree("boostbook")
# For some reason, the messages are sent to stderr.
t.run_build_system()
t.fail_test(t.stdout().find("""Writing boost/A.html for refentry(boost.A)
Writing library/reference.html for section(library.reference)
Writing index.html for chapter(library)
Writing docs_HTML.manifest
""") == -1)
t.expect_addition(["html/boost/A.html", "html/index.html"])
t.cleanup()

View File

@@ -0,0 +1,16 @@
/* Copyright 2004, 2006 Vladimir Prus */
/* Distributed under the Boost Software License, Version 1.0. */
/* (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) */
// Seems like Boostbook does like classes outside of namespaces,
// and won't generate anything for them.
namespace boost {
/// A class
class A {
public:
/// A constructor
A();
};
}

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN"
"http://www.boost.org/tools/boostbook/dtd/boostbook.dtd">
<!-- Copyright 2004 Vladimir Prus -->
<!-- Distributed under the Boost Software License, Version 1.0. -->
<!-- (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) -->
<library
name="library"
dirname="librarys" id="library"
xmlns:xi="http://www.w3.org/2001/XInclude">
<libraryinfo>
<author>
<firstname>Joe</firstname>
<surname>Hacker</surname>
</author>
<copyright>
<year>7002</year>
<holder>Joe Hacker</holder>
</copyright>
</libraryinfo>
<title>Documentation</title>
<section>
<title>Introduction</title>
<para>This is introduction</para>
</section>
<xi:include href="autodoc.xml"/>
</library>

View File

@@ -0,0 +1,3 @@
boostbook docs : docs.xml autodoc ;
doxygen autodoc : [ glob *.hpp ] ;

View File

@@ -0,0 +1,107 @@
#!/usr/bin/python
# Copyright 2003 Dave Abrahams
# Copyright 2002, 2003, 2005 Vladimir Prus
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
# Test that we can change build directory using the 'build-dir' project
# attribute.
import BoostBuild
import string
import os
t = BoostBuild.Tester(use_test_config=False)
# Test that top-level project can affect build dir.
t.write("jamroot.jam", "import gcc ;")
t.write("jamfile.jam", """\
project : build-dir build ;
exe a : a.cpp ;
build-project src ;
""")
t.write("a.cpp", "int main() {}\n")
t.write("src/jamfile.jam", "exe b : b.cpp ; ")
t.write("src/b.cpp", "int main() {}\n")
t.run_build_system()
t.expect_addition(["build/$toolset/debug*/a.exe",
"build/src/$toolset/debug*/b.exe"])
# Test that building from child projects work.
t.run_build_system(subdir='src')
t.ignore("build/config.log")
t.ignore("build/project-cache.jam")
t.expect_nothing_more()
# Test that project can override build dir.
t.write("jamfile.jam", """\
exe a : a.cpp ;
build-project src ;
""")
t.write("src/jamfile.jam", """\
project : build-dir build ;
exe b : b.cpp ;
""")
t.run_build_system()
t.expect_addition(["bin/$toolset/debug*/a.exe",
"src/build/$toolset/debug*/b.exe"])
# Now test the '--build-dir' option.
t.rm(".")
t.write("jamroot.jam", "")
# Test that we get an error when no project id is specified.
t.run_build_system(["--build-dir=foo"])
t.fail_test(t.stdout().find(
"warning: the --build-dir option will be ignored") == -1)
t.write("jamroot.jam", """\
project foo ;
exe a : a.cpp ;
build-project sub ;
""")
t.write("a.cpp", "int main() {}\n")
t.write("sub/jamfile.jam", "exe b : b.cpp ;\n")
t.write("sub/b.cpp", "int main() {}\n")
t.run_build_system(["--build-dir=build"])
t.expect_addition(["build/foo/$toolset/debug*/a.exe",
"build/foo/sub/$toolset/debug*/b.exe"])
t.write("jamroot.jam", """\
project foo : build-dir bin.v2 ;
exe a : a.cpp ;
build-project sub ;
""")
t.run_build_system(["--build-dir=build"])
t.expect_addition(["build/foo/bin.v2/$toolset/debug*/a.exe",
"build/foo/bin.v2/sub/$toolset/debug*/b.exe"])
# Try building in subdir. We expect that the entire build tree with be in
# 'sub/build'. Today, I am not sure if this is what the user expects, but let
# it be.
t.rm('build')
t.run_build_system(["--build-dir=build"], subdir="sub")
t.expect_addition(["sub/build/foo/bin.v2/sub/$toolset/debug*/b.exe"])
t.write("jamroot.jam", """\
project foo : build-dir %s ;
exe a : a.cpp ;
build-project sub ;
""" % os.getcwd().replace('\\', '\\\\'))
t.run_build_system(["--build-dir=build"], status=1)
t.fail_test(t.stdout().find(
"Absolute directory specified via 'build-dir' project attribute") == -1)
t.cleanup()

View File

@@ -0,0 +1,170 @@
#!/usr/bin/python
# Copyright (C) 2006. Vladimir Prus
# Copyright (C) 2008. Jurko Gospodnetic
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
# Tests that we explicitly request a file (not target) to be built by
# specifying its name on the command line.
import BoostBuild
###############################################################################
#
# test_building_file_from_specific_project()
# ------------------------------------------
#
###############################################################################
def test_building_file_from_specific_project():
t = BoostBuild.Tester(use_test_config=False)
t.write("jamroot.jam", """\
exe hello : hello.cpp ;
exe hello2 : hello.cpp ;
build-project sub ;
""")
t.write("hello.cpp", "int main() {}\n")
t.write("sub/jamfile.jam", """
exe hello : hello.cpp ;
exe hello2 : hello.cpp ;
exe sub : hello.cpp ;
""")
t.write("sub/hello.cpp", "int main() {}\n")
t.run_build_system(["sub", t.adjust_suffix("hello.obj")])
t.expect_output_lines("*depends on itself*", False)
t.expect_addition("sub/bin/$toolset/debug*/hello.obj")
t.expect_nothing_more()
t.cleanup()
###############################################################################
#
# test_building_file_from_specific_target()
# -----------------------------------------
#
###############################################################################
def test_building_file_from_specific_target():
t = BoostBuild.Tester(use_test_config=False)
t.write("jamroot.jam", """\
exe hello1 : hello1.cpp ;
exe hello2 : hello2.cpp ;
exe hello3 : hello3.cpp ;
""")
t.write("hello1.cpp", "int main() {}\n")
t.write("hello2.cpp", "int main() {}\n")
t.write("hello3.cpp", "int main() {}\n")
t.run_build_system(["hello1", t.adjust_suffix("hello1.obj")])
t.expect_addition("bin/$toolset/debug*/hello1.obj")
t.expect_nothing_more()
t.cleanup()
###############################################################################
#
# test_building_missing_file_from_specific_target()
# -------------------------------------------------
#
###############################################################################
def test_building_missing_file_from_specific_target():
t = BoostBuild.Tester(use_test_config=False)
t.write("jamroot.jam", """\
exe hello1 : hello1.cpp ;
exe hello2 : hello2.cpp ;
exe hello3 : hello3.cpp ;
""")
t.write("hello1.cpp", "int main() {}\n")
t.write("hello2.cpp", "int main() {}\n")
t.write("hello3.cpp", "int main() {}\n")
obj = t.adjust_suffix("hello2.obj")
t.run_build_system(["hello1", obj], status=1)
t.expect_output_lines("don't know how to make*" + obj)
t.expect_nothing_more()
t.cleanup()
###############################################################################
#
# test_building_multiple_files_with_different_names()
# ---------------------------------------------------
#
###############################################################################
def test_building_multiple_files_with_different_names():
t = BoostBuild.Tester(use_test_config=False)
t.write("jamroot.jam", """\
exe hello1 : hello1.cpp ;
exe hello2 : hello2.cpp ;
exe hello3 : hello3.cpp ;
""")
t.write("hello1.cpp", "int main() {}\n")
t.write("hello2.cpp", "int main() {}\n")
t.write("hello3.cpp", "int main() {}\n")
t.run_build_system([t.adjust_suffix("hello1.obj"), t.adjust_suffix(
"hello2.obj")])
t.expect_addition("bin/$toolset/debug*/hello1.obj")
t.expect_addition("bin/$toolset/debug*/hello2.obj")
t.expect_nothing_more()
t.cleanup()
###############################################################################
#
# test_building_multiple_files_with_the_same_name()
# -------------------------------------------------
#
###############################################################################
def test_building_multiple_files_with_the_same_name():
t = BoostBuild.Tester(use_test_config=False)
t.write("jamroot.jam", """\
exe hello : hello.cpp ;
exe hello2 : hello.cpp ;
build-project sub ;
""")
t.write("hello.cpp", "int main() {}\n")
t.write("sub/jamfile.jam", """
exe hello : hello.cpp ;
exe hello2 : hello.cpp ;
exe sub : hello.cpp ;
""")
t.write("sub/hello.cpp", "int main() {}\n")
t.run_build_system([t.adjust_suffix("hello.obj")])
t.expect_output_lines("*depends on itself*", False)
t.expect_addition("bin/$toolset/debug*/hello.obj")
t.expect_addition("sub/bin/$toolset/debug*/hello.obj")
t.expect_nothing_more()
t.cleanup()
###############################################################################
#
# main()
# ------
#
###############################################################################
test_building_file_from_specific_project()
test_building_file_from_specific_target()
test_building_missing_file_from_specific_target()
test_building_multiple_files_with_different_names()
test_building_multiple_files_with_the_same_name()

View File

@@ -0,0 +1,39 @@
#!/usr/bin/python
# Copyright (C) Vladimir Prus 2006.
# Distributed under the Boost Software License, Version 1.0. (See
# accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
# Tests add-pre-build-hook and add-post-build-hook
import BoostBuild
t = BoostBuild.Tester()
t.write("Jamroot.jam", """
import build-system ;
build-system.add-pre-build-hook pre-build ;
build-system.add-post-build-hook post-build ;
rule pre-build ( )
{
ECHO "in" pre-build hook ;
}
rule post-build ( okay ? )
{
ECHO "in" post-build hook $(okay) ;
}
message show : building main targets ;
""")
t.run_build_system(stdout="""\
building main targets
in pre-build hook
...found 1 target...
in post-build hook ok
""")
t.cleanup()

View File

@@ -0,0 +1,23 @@
#!/usr/bin/python
# Copyright (C) Vladimir Prus 2006.
# Distributed under the Boost Software License, Version 1.0. (See
# accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
# Tests that <build>no property prevents a target from being built.
import BoostBuild
t = BoostBuild.Tester(use_test_config=False)
t.write("jamroot.jam", "exe hello : hello.cpp : <variant>debug:<build>no ;")
t.write("hello.cpp", "int main() {}\n")
t.run_build_system()
t.expect_nothing_more()
t.run_build_system(["release"])
t.expect_addition("bin/$toolset/release*/hello.exe")
t.cleanup()

View File

@@ -0,0 +1,30 @@
#!/usr/bin/python
# Copyright 2012 Steven Watanabe
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
# This tests the ECHO rule.
import BoostBuild
def test_echo(name):
t = BoostBuild.Tester(["-ffile.jam"], pass_toolset=0)
t.write("file.jam", """\
%s ;
UPDATE ;
""" % name)
t.run_build_system(stdout="\n")
t.write("file.jam", """\
%s a message ;
UPDATE ;
""" % name)
t.run_build_system(stdout="a message\n")
t.cleanup()
test_echo("ECHO")
test_echo("Echo")
test_echo("echo")

View File

@@ -0,0 +1,42 @@
#!/usr/bin/python
# Copyright 2012 Steven Watanabe
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
# This tests the EXIT rule.
import BoostBuild
def test_exit(name):
t = BoostBuild.Tester(["-ffile.jam"], pass_toolset=0)
t.write("file.jam", "%s ;" % name)
t.run_build_system(status=1, stdout="\n")
t.rm(".")
t.write("file.jam", "%s : 0 ;" % name)
t.run_build_system(stdout="\n")
t.rm(".")
t.write("file.jam", "%s : 1 ;" % name)
t.run_build_system(status=1, stdout="\n")
t.rm(".")
t.write("file.jam", "%s : 2 ;" % name)
t.run_build_system(status=2, stdout="\n")
t.rm(".")
t.write("file.jam", "%s a message ;" % name)
t.run_build_system(status=1, stdout="a message\n")
t.rm(".")
t.write("file.jam", "%s a message : 0 ;" % name)
t.run_build_system(stdout="a message\n")
t.rm(".")
t.cleanup()
test_exit("EXIT")
test_exit("Exit")
test_exit("exit")

View File

@@ -0,0 +1,87 @@
#!/usr/bin/python
# Copyright 2014 Steven Watanabe
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
# This tests the GLOB rule.
import os
import BoostBuild
def test_glob(files, glob, expected, setup=""):
t = BoostBuild.Tester(["-ffile.jam"], pass_toolset=0)
t.write("file.jam", setup + """
for local p in [ SORT %s ]
{
ECHO $(p) ;
}
UPDATE ;
""" % glob)
for f in files:
t.write(f, "")
# convert / into \ on windows
expected = [os.path.join(*p.split("/")) for p in expected]
expected.sort()
t.run_build_system(stdout="\n".join(expected + [""]))
t.cleanup()
# one or both arguments empty
test_glob([], "[ GLOB : ]", [])
test_glob([], "[ GLOB . : ]", [])
test_glob([], "[ GLOB : * ]", [])
# a single result
test_glob([], "[ GLOB . : * ]", ["./file.jam"])
# * can match any number of characters
test_glob([], "[ GLOB . : file*.jam ]", ["./file.jam"])
test_glob([], "[ GLOB . : f*am ]", ["./file.jam"])
# ? should match a single character, but not more than one
test_glob([], "[ GLOB . : fi?e.?am ]", ["./file.jam"])
test_glob([], "[ GLOB . : fi?.jam ]", [])
# [abc-fh-j] matches a set of characters
test_glob([], '[ GLOB . : "[f][i][l][e].jam" ]', ["./file.jam"])
test_glob([], '[ GLOB . : "[fghau][^usdrwe][k-o][^f-s].jam" ]', ["./file.jam"])
# \x matches x
test_glob([], "[ GLOB . : \\f\\i\\l\\e.jam ]", ["./file.jam"])
# multiple results
test_glob(["test.txt"], "[ GLOB . : * ]", ["./file.jam", "./test.txt"])
# directories
test_glob(["dir1/dir2/test.txt"], "[ GLOB dir1 : * ]", ["dir1/dir2"]);
# non-existent directory
test_glob([], "[ GLOB dir1 : * ] ", [])
# multiple directories and patterns
test_glob(["dir1/file1.txt", "dir2/file1.txt",
"dir2/file2.txt"],
"[ GLOB dir1 dir2 : file1* file2* ]",
["dir1/file1.txt", "dir2/file1.txt",
"dir2/file2.txt"])
# The directory can contain . and ..
test_glob(["dir/test.txt"], "[ GLOB dir/. : test.txt ]", ["dir/./test.txt"])
test_glob(["dir/test.txt"], "[ GLOB dir/.. : file.jam ]", ["dir/../file.jam"])
# On case insensitive filesystems, the result should
# be normalized. It should NOT be downcased.
test_glob(["TEST.TXT"], "[ GLOB . : TEST.TXT ]", ["./TEST.TXT"])
case_insensitive = (os.path.normcase("FILE") == "file")
if case_insensitive:
test_glob(["TEST.TXT"], "[ GLOB . : test.txt ]", ["./TEST.TXT"])
# This used to fail because the caching routines incorrectly
# reported that . and .. do not exist.
test_glob(["D1/D2/TEST.TXT"], "[ GLOB D1/./D2 : test.txt ]",
["D1/./D2/TEST.TXT"])
test_glob(["D1/TEST.TXT", "TEST.TXT"], "[ GLOB D1/../D1 : test.txt ]",
["D1/../D1/TEST.TXT"])
# This also failed because directories that were first found
# by GLOB were recorded as non-existent.
test_glob(["D1/D2/TEST.TXT"], "[ GLOB d1/d2 : test.txt ]",
["D1/D2/TEST.TXT"],
"GLOB . : * ;")

View File

@@ -0,0 +1,217 @@
#!/usr/bin/python
# Copyright 2014 Steven Watanabe
# Copyright 2015 Artur Shepilko
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
# This tests the GLOB_ARCHIVE rule.
import os
import sys
try:
from StringIO import StringIO
except:
from io import StringIO
import BoostBuild
vms = ( os.name == 'posix' and sys.platform == 'OpenVMS')
t = BoostBuild.Tester()
## Setup test archive sources and symbols they contain.
sources = {
"a.cpp" : ["a"],
"b.cpp" : ["b"],
"b_match.cpp" : ["b_match"],
"c/nopath_check.cpp" : ["nopath_check"],
"CaseCheck.cpp" : ["CaseCheck"],
"seq_check1.cpp" : ["seq_check1"],
"seq_check2.cpp" : ["seq_check2"],
"seq_check3.cpp" : ["seq_check3"],
"symbols_check.c" : ["symbol", "symbol_match"],
"members_and_symbols_check.c" : ["member_and_symbol_match"],
"symbol_case_check.c" : ["SymbolCaseCheck"],
"main_check.cpp" : ["main"]
}
def create_sources(path, sources):
for s in sources :
f = os.path.join(path, s)
t.write(f, "")
output = StringIO()
for sym in sources[s] :
output.write("int %s() { return 0; }\n" % sym)
t.write(f, output.getvalue())
def setup_archive(name, sources):
global archive
global obj_suffix
archive = t.adjust_names(name)[0]
obj_suffix = t.adjust_names(".obj")[0]
output = StringIO()
t.write("jamroot.jam","")
output.write("""\
static-lib %s :
""" % name.split(".")[0])
## sort the sources, so we can test order of the globbed members
for s in sorted(sources) :
output.write("""\
%s
""" % s)
output.write("""\
;
""")
t.write("lib/jamfile.jam", output.getvalue())
create_sources("lib", sources)
t.run_build_system(subdir="lib")
built_archive = "lib/bin/$toolset/debug*/%s" % name
t.expect_addition(built_archive)
t.copy(built_archive, name)
t.rm("lib")
def test_glob_archive(archives, glob, expected, sort_results = False):
output = StringIO()
## replace placeholders
glob = glob.replace("$archive1", archives[0]).replace("$obj", obj_suffix)
expected = [ m.replace("$archive1",
archives[0]).replace("$obj", obj_suffix) for m in expected ]
if len(archives) > 1 :
glob = glob.replace("$archive2", archives[1]).replace("$obj", obj_suffix)
expected = [ m.replace("$archive2",
archives[1]).replace("$obj", obj_suffix) for m in expected ]
## create test jamfile
if sort_results : glob = "[ SORT %s ]" % glob
output.write("""\
for local p in %s
{
ECHO $(p) ;
}
UPDATE ;
""" % glob)
t.write("file.jam", output.getvalue())
## run test jamfile and match against expected results
if sort_results : expected.sort()
t.run_build_system(["-ffile.jam"], stdout="\n".join(expected + [""]))
t.rm("file.jam")
## RUN TESTS
setup_archive("auxilliary1.lib", sources)
archive1 = archive
setup_archive("auxilliary2.lib", sources)
archive2 = archive
## all arguments empty
test_glob_archive([archive1], "[ GLOB_ARCHIVE ]", [])
## empty query
test_glob_archive([archive1], "[ GLOB_ARCHIVE $archive1 : ]", [])
## no-match
test_glob_archive([archive1], "[ GLOB_ARCHIVE $archive1 : a ]", [])
## match exact
test_glob_archive([archive1], "[ GLOB_ARCHIVE $archive1 : a$obj ]",
["$archive1(a$obj)"])
## glob wildcards:1
test_glob_archive([archive1], "[ GLOB_ARCHIVE $archive1 : b.* ]",
["$archive1(b$obj)"])
## glob wildcards:2
test_glob_archive([archive1], '[ GLOB_ARCHIVE $archive1 : "\\b?match[\.]*" ]',
["$archive1(b_match$obj)"])
## glob wildcards:3
test_glob_archive([archive1], "[ SORT [ GLOB_ARCHIVE $archive1 : b* ] ]",
["$archive1(b$obj)", "$archive1(b_match$obj)"])
## glob multiple patterns with multiple results.
test_glob_archive([archive1], "[ SORT [ GLOB_ARCHIVE $archive1 : b.* b_* ] ]",
["$archive1(b$obj)", "$archive1(b_match$obj)"])
## glob multiple archives and patterns.
test_glob_archive([archive1, archive2],
"[ SORT [ GLOB_ARCHIVE $archive1 $archive2 : b.* b_* ] ]",
["$archive1(b$obj)", "$archive1(b_match$obj)",
"$archive2(b$obj)", "$archive2(b_match$obj)"])
## glob same archive multiple times.
test_glob_archive([archive1, archive1],
"[ GLOB_ARCHIVE $archive1 $archive2 $archive1 : b.* ]",
["$archive1(b$obj)", "$archive2(b$obj)", "$archive1(b$obj)"])
## returned archive member has no path, even though its source object-file did.
## this is rather NT-specific, where members also store their object-file's path.
test_glob_archive([archive1], "[ GLOB_ARCHIVE $archive1 : nopath_check$obj ]",
["$archive1(nopath_check$obj)"])
## case insensitive matching, when archives support case sensitive member names.
## VMS implementation forces case-insensitive matching and downcased member names.
case_sensitive_members = ( not vms )
if case_sensitive_members:
test_glob_archive([archive1],
"[ GLOB_ARCHIVE $archive1 : casecheck$obj : true ]",
["$archive1(CaseCheck$obj)"])
elif vms:
test_glob_archive([archive1],
"[ GLOB_ARCHIVE $archive1 : CaseCheck$obj : false ]",
["$archive1(casecheck$obj)"])
## test the order of matched members, in general it should match the
## insertion sequence.
test_glob_archive([archive1], "[ SORT [ GLOB_ARCHIVE $archive1 : seq_check*$obj ] ]",
["$archive1(seq_check1$obj)", "$archive1(seq_check2$obj)",
"$archive1(seq_check3$obj)"])
## glob members by symbols they contain.
## Currently supported only on VMS.
symbol_glob_supported = ( vms )
if symbol_glob_supported :
## NOTE: generated symbols are compiler-dependent and may be specifically
## mangled (as in C++ case), so globbing by exact symbol is non-trivial.
## However, C-generated symbols are likely to have more portable names,
## so for the glob-by-symbol tests we glob C-generated archive members.
## glob members by exact symbol.
test_glob_archive([archive1],
"[ GLOB_ARCHIVE $archive1 : : : symbol ]",
["$archive1(symbols_check$obj)"])
## glob members by symbol wildcard.
test_glob_archive([archive1],
"[ GLOB_ARCHIVE $archive1 : : : symbol_* ]",
["$archive1(symbols_check$obj)"])
## glob members by member pattern AND symbol pattern.
test_glob_archive([archive1],
"[ GLOB_ARCHIVE $archive1 : *symbol* : : *member* ]",
["$archive1(members_and_symbols_check$obj)"])
## case insensitive symbol glob.
test_glob_archive([archive1],
"[ GLOB_ARCHIVE $archive1 : : true : symbolcasecheck ]",
["$archive1(symbol_case_check$obj)"])
## glob member that contains main symbol.
test_glob_archive([archive1],
"[ GLOB_ARCHIVE $archive1 : : : main _main ]",
["$archive1(main_check$obj)"])
else:
test_glob_archive([archive1],
"[ GLOB_ARCHIVE $archive1 : : : symbol ]",
[])
t.cleanup()

View File

@@ -0,0 +1,31 @@
#!/usr/bin/python
# Copyright 2012 Steven Watanabe
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
import BoostBuild
import os
import sys
t = BoostBuild.Tester(pass_toolset=0)
t.write("link-target", "")
try:
os.symlink("link-target", "link")
except (AttributeError, OSError) as e:
# Either OS does not support symlinks or not enough privilege
print("XFAIL: %s" % e)
t.cleanup()
sys.exit()
t.write("file.jam", """
ECHO [ READLINK link ] ;
EXIT [ READLINK link-target ] : 0 ;
""")
t.run_build_system(["-ffile.jam"], stdout="""link-target
""")
t.cleanup()

View File

@@ -0,0 +1,57 @@
#!/usr/bin/python
# Copyright 2012. Jurko Gospodnetic
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
# This tests the SPLIT_BY_CHARACTERS rule.
import BoostBuild
def test_invalid(params, expected_error_line):
t = BoostBuild.Tester(pass_toolset=0)
t.write("file.jam", "SPLIT_BY_CHARACTERS %s ;" % params)
t.run_build_system(["-ffile.jam"], status=1)
t.expect_output_lines("[*] %s" % expected_error_line)
t.cleanup()
def test_valid():
t = BoostBuild.Tester(pass_toolset=0)
t.write("jamroot.jam", """\
import assert ;
assert.result FooBarBaz : SPLIT_BY_CHARACTERS FooBarBaz : "" ;
assert.result FooBarBaz : SPLIT_BY_CHARACTERS FooBarBaz : x ;
assert.result FooBa Baz : SPLIT_BY_CHARACTERS FooBarBaz : r ;
assert.result FooBa Baz : SPLIT_BY_CHARACTERS FooBarBaz : rr ;
assert.result FooBa Baz : SPLIT_BY_CHARACTERS FooBarBaz : rrr ;
assert.result FooB rB z : SPLIT_BY_CHARACTERS FooBarBaz : a ;
assert.result FooB B z : SPLIT_BY_CHARACTERS FooBarBaz : ar ;
assert.result ooBarBaz : SPLIT_BY_CHARACTERS FooBarBaz : F ;
assert.result FooBarBa : SPLIT_BY_CHARACTERS FooBarBaz : z ;
assert.result ooBarBa : SPLIT_BY_CHARACTERS FooBarBaz : Fz ;
assert.result F B rB z : SPLIT_BY_CHARACTERS FooBarBaz : oa ;
assert.result Alib b : SPLIT_BY_CHARACTERS Alibaba : oa ;
assert.result libaba : SPLIT_BY_CHARACTERS Alibaba : oA ;
assert.result : SPLIT_BY_CHARACTERS FooBarBaz : FooBarBaz ;
assert.result : SPLIT_BY_CHARACTERS FooBarBaz : FoBarz ;
# Questionable results - should they return an empty string or an empty list?
assert.result : SPLIT_BY_CHARACTERS "" : "" ;
assert.result : SPLIT_BY_CHARACTERS "" : x ;
assert.result : SPLIT_BY_CHARACTERS "" : r ;
assert.result : SPLIT_BY_CHARACTERS "" : rr ;
assert.result : SPLIT_BY_CHARACTERS "" : rrr ;
assert.result : SPLIT_BY_CHARACTERS "" : oa ;
""")
t.run_build_system()
t.cleanup()
test_invalid("", "missing argument string")
test_invalid("Foo", "missing argument delimiters")
test_invalid(": Bar", "missing argument string")
test_invalid("a : b : c", "extra argument c")
test_invalid("a b : c", "extra argument b")
test_invalid("a : b c", "extra argument c")
test_valid()

View File

@@ -0,0 +1,119 @@
#!/usr/bin/python
# Copyright (C) 2013 Steven Watanabe
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
import BoostBuild
import MockToolset
t = BoostBuild.Tester(arguments=['toolset=mock', '--ignore-site-config', '--user-config='], pass_toolset=0)
MockToolset.create(t)
# Build from source
t.write("bzip2/bzlib.h", 'bzip2')
t.write("bzip2/blocksort.c", 'blocksort')
t.write("Jamroot.jam", """
path-constant here : . ;
using bzip2 : : <source>$(here)/bzip2 ;
alias bzip2 : /bzip2//bzip2 : : <link>static <link>shared ;
""")
MockToolset.set_expected(t, '''
source_file('blocksort.c', 'blocksort')
action('-c -x c -I./bzip2 -o $blocksort.o $blocksort.c')
action('--dll $blocksort.o -o $bz2.so')
action('--archive $blocksort.o -o $bz2.a')
''')
t.run_build_system()
t.expect_addition('bin/standalone/bzip2/mock/debug/bz2.dll')
t.expect_addition('bin/standalone/bzip2/mock/debug/link-static/bz2.lib')
t.rm('bzip2')
# Generic definitions that aren't configuration specific
common_stuff = '''
source_file('test.cpp', 'test.cpp')
source_file('main.cpp', 'int main() {}')
source_file('bzlib.h.cpp', '#include <bzlib.h>\\n')
action('-c -x c++ $main.cpp -o $main.o')
'''
t.write('test.cpp', 'test.cpp')
# Default initialization - static library
t.rm('bin')
t.write("Jamroot.jam", """
path-constant here : . ;
using bzip2 ;
exe test : test.cpp /bzip2//bzip2 : : <link>static <link>shared ;
""")
MockToolset.set_expected(t, common_stuff + '''
action('$main.o --static-lib=bz2 -o $config.exe')
action('-c -x c++ $bzlib.h.cpp -o $bzlib.h.o')
action('-c -x c++ $test.cpp -o $test.o')
action('$test.o --static-lib=bz2 -o $test')
''')
t.run_build_system()
t.expect_addition('bin/mock/debug/test.exe')
t.expect_addition('bin/mock/debug/link-static/test.exe')
# Default initialization - shared library
t.rm('bin')
t.write("Jamroot.jam", """
path-constant here : . ;
using bzip2 ;
exe test : test.cpp /bzip2//bzip2 : : <link>static <link>shared ;
""")
MockToolset.set_expected(t, common_stuff + '''
action('$main.o --shared-lib=bz2 -o $config.exe')
action('-c -x c++ $bzlib.h.cpp -o $bzlib.h.o')
action('-c -x c++ $test.cpp -o $test.o')
action('$test.o --shared-lib=bz2 -o $test')
''')
t.run_build_system()
t.expect_addition('bin/mock/debug/test.exe')
t.expect_addition('bin/mock/debug/link-static/test.exe')
# Initialization in explicit location - static library
t.rm('bin')
t.write("Jamroot.jam", """
path-constant here : . ;
using bzip2 : : <name>mybzlib <include>$(here)/bzip2 <search>$(here)/bzip2 ;
exe test : test.cpp /bzip2//bzip2 : : <link>static <link>shared ;
""")
t.write('bzip2/bzlib.h', 'bzip2')
MockToolset.set_expected(t, common_stuff + '''
action('$main.o -L./bzip2 --static-lib=mybzlib -o $config.exe')
action('-c -x c++ $test.cpp -I./bzip2 -o $test.o')
action('$test.o -L./bzip2 --static-lib=mybzlib -o $test')
''')
t.run_build_system()
t.expect_addition('bin/mock/debug/test.exe')
t.expect_addition('bin/mock/debug/link-static/test.exe')
# Initialization in explicit location - shared library
t.rm('bin')
t.write("Jamroot.jam", """
path-constant here : . ;
using bzip2 : : <name>mybzlib <include>$(here)/bzip2 <search>$(here)/bzip2 ;
exe test : test.cpp /bzip2//bzip2 : : <link>static <link>shared ;
""")
MockToolset.set_expected(t, common_stuff + '''
action('$main.o -L./bzip2 --shared-lib=mybzlib -o $config.exe')
action('-c -x c++ $test.cpp -I./bzip2 -o $test.o')
action('$test.o -L./bzip2 --shared-lib=mybzlib -o $test')
''')
t.run_build_system()
t.expect_addition('bin/mock/debug/test.exe')
t.expect_addition('bin/mock/debug/link-static/test.exe')
t.cleanup()

View File

@@ -0,0 +1,36 @@
#!/usr/bin/python
# Copyright 2003 Vladimir Prus
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
# Test that C files are compiled by a C compiler.
import BoostBuild
t = BoostBuild.Tester(use_test_config=False)
t.write("jamroot.jam", """
project ;
exe hello : hello.cpp a.c ;
""")
t.write("hello.cpp", """
extern "C" int foo();
int main() { return foo(); }
""")
t.write("a.c", """
// This will not compile unless in C mode.
int foo()
{
int new = 0;
new = (new+1)*7;
return new;
}
""")
t.run_build_system()
t.expect_addition("bin/$toolset/debug*/hello.exe")
t.cleanup()

View File

@@ -0,0 +1,56 @@
#!/usr/bin/python
# Copyright 2003 Dave Abrahams
# Copyright 2002, 2003 Vladimir Prus
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
# This tests that :
# 1) the 'make' correctly assigns types to produced targets
# 2) if 'make' creates targets of type CPP, they are correctly used.
import BoostBuild
t = BoostBuild.Tester(use_test_config=False)
# In order to correctly link this app, 'b.cpp', created by a 'make' rule, should
# be compiled.
t.write("jamroot.jam", "import gcc ;")
t.write("jamfile.jam", r'''
import os ;
if [ os.name ] = NT
{
actions create
{
echo int main() {} > $(<)
}
}
else
{
actions create
{
echo "int main() {}" > $(<)
}
}
IMPORT $(__name__) : create : : create ;
exe a : l dummy.cpp ;
# Needs to be a static lib for Windows - main() cannot appear in DLL.
static-lib l : a.cpp b.cpp ;
make b.cpp : : create ;
''')
t.write("a.cpp", "")
t.write("dummy.cpp", "// msvc needs at least one object file\n")
t.run_build_system()
t.expect_addition("bin/$toolset/debug*/a.exe")
t.cleanup()

View File

@@ -0,0 +1,104 @@
#!/usr/bin/python
# Copyright (C) Vladimir Prus 2006.
# Distributed under the Boost Software License, Version 1.0. (See
# accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
import BoostBuild
t = BoostBuild.Tester(use_test_config=False)
t.write("a.cpp", "int main() {}\n")
t.write("jamroot.jam", "exe a : a.cpp sub1//sub1 sub2//sub2 sub3//sub3 ;")
t.write("sub1/jamfile.jam", """\
lib sub1 : sub1.cpp sub1_2 ../sub2//sub2 ;
lib sub1_2 : sub1_2.cpp ;
""")
t.write("sub1/sub1.cpp", """\
#ifdef _WIN32
__declspec(dllexport)
#endif
void sub1() {}
""")
t.write("sub1/sub1_2.cpp", """\
#ifdef _WIN32
__declspec(dllexport)
#endif
void sub1() {}
""")
t.write("sub2/jamfile.jam", "lib sub2 : sub2.cpp ;")
t.write("sub2/sub2.cpp", """\
#ifdef _WIN32
__declspec(dllexport)
#endif
void sub2() {}
""")
t.write("sub3/jamroot.jam", "lib sub3 : sub3.cpp ;")
t.write("sub3/sub3.cpp", """\
#ifdef _WIN32
__declspec(dllexport)
#endif
void sub3() {}
""")
# 'clean' should not remove files under separate jamroot.jam.
t.run_build_system()
t.run_build_system(["--clean"])
t.expect_removal("bin/$toolset/debug*/a.obj")
t.expect_removal("sub1/bin/$toolset/debug*/sub1.obj")
t.expect_removal("sub1/bin/$toolset/debug*/sub1_2.obj")
t.expect_removal("sub2/bin/$toolset/debug*/sub2.obj")
t.expect_nothing("sub3/bin/$toolset/debug*/sub3.obj")
# 'clean-all' removes everything it can reach.
t.run_build_system()
t.run_build_system(["--clean-all"])
t.expect_removal("bin/$toolset/debug*/a.obj")
t.expect_removal("sub1/bin/$toolset/debug*/sub1.obj")
t.expect_removal("sub1/bin/$toolset/debug*/sub1_2.obj")
t.expect_removal("sub2/bin/$toolset/debug*/sub2.obj")
t.expect_nothing("sub3/bin/$toolset/debug*/sub3.obj")
# 'clean' together with project target removes only under that project.
t.run_build_system()
t.run_build_system(["sub1", "--clean"])
t.expect_nothing("bin/$toolset/debug*/a.obj")
t.expect_removal("sub1/bin/$toolset/debug*/sub1.obj")
t.expect_removal("sub1/bin/$toolset/debug*/sub1_2.obj")
t.expect_nothing("sub2/bin/$toolset/debug*/sub2.obj")
t.expect_nothing("sub3/bin/$toolset/debug*/sub3.obj")
# 'clean-all' removes everything.
t.run_build_system()
t.run_build_system(["sub1", "--clean-all"])
t.expect_nothing("bin/$toolset/debug*/a.obj")
t.expect_removal("sub1/bin/$toolset/debug*/sub1.obj")
t.expect_removal("sub1/bin/$toolset/debug*/sub1_2.obj")
t.expect_removal("sub2/bin/$toolset/debug*/sub2.obj")
t.expect_nothing("sub3/bin/$toolset/debug*/sub3.obj")
# If main target is explicitly named, we should not remove files from other
# targets.
t.run_build_system()
t.run_build_system(["sub1//sub1", "--clean"])
t.expect_removal("sub1/bin/$toolset/debug*/sub1.obj")
t.expect_nothing("sub1/bin/$toolset/debug*/sub1_2.obj")
t.expect_nothing("sub2/bin/$toolset/debug*/sub2.obj")
t.expect_nothing("sub3/bin/$toolset/debug*/sub3.obj")
# Regression test: sources of the 'cast' rule were mistakenly deleted.
t.rm(".")
t.write("jamroot.jam", """\
import cast ;
cast a cpp : a.h ;
""")
t.write("a.h", "")
t.run_build_system(["--clean"])
t.expect_nothing("a.h")
t.cleanup()

View File

@@ -0,0 +1,41 @@
#!/usr/bin/python
# Copyright 2015 Aaron Boman
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
# Test that free property inside.
import BoostBuild
t = BoostBuild.Tester(use_test_config=False)
t.write("jamroot.jam", "")
t.write(
"subdir/build.jam",
"""
import feature ;
feature.feature my-feature : : free ;
"""
)
t.write(
"subdir/subsubdir/build.jam",
"""
exe hello : hello.c ;
"""
)
t.write(
"subdir/subsubdir/hello.c",
r"""
#include <stdio.h>
int main(int argc, char **argv){
printf("%s\n", "Hello, World!");
}
"""
)
# run from the root directory
t.run_build_system(['subdir/subsubdir', 'my-feature="some value"'])
t.cleanup()

View File

@@ -0,0 +1,341 @@
#!/usr/bin/python
# Copyright 2012 Jurko Gospodnetic
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
# Temporarily enabled dummy test that always fails and is used to collect
# extra debugging information from Boost Build test runner sites.
import BoostBuild
import os
import re
import sys
###############################################################################
#
# Public interface.
#
###############################################################################
def collectDebugInfo():
t = _init()
global tag
tag = "Python version"
try:
_info(sys.version)
except:
_info_exc()
tag = "Python platform"
try:
_info(sys.platform)
except:
_info_exc()
tag = "Boost Jam/Build version"
try:
_infoX(_getJamVersionInfo(t))
except:
_info_exc()
#_collectDebugInfo_environ()
# Report prepared annotations.
t.fail_test(1, dump_difference=False, dump_stdio=False, dump_stack=False)
###############################################################################
#
# Private interface.
#
###############################################################################
varSeparator = "###$^%~~~"
def _collect(results, prefix, name, t):
results.append("%s - %s - os.getenv(): %r" % (prefix, name, os.getenv(
name)))
results.append("%s - %s - os.environ.get(): %r" % (prefix, name,
os.environ.get(name)))
external_values = _getExternalValues(t, name)
results.append("%s - %s - external: %r" % (prefix, name,
external_values[name]))
def _collectDebugInfo_environ(t):
dummyVars = ["WOOF_WOOFIE_%d" % x for x in range(4)]
global tag
tag = "XXX in os.environ"
try:
def f(name):
return "%s: %s" % (name, name in os.environ)
_infoX(f(x) for x in dummyVars)
except:
_info_exc()
tag = "os.environ[XXX]"
try:
def f(name):
try:
result = os.environ[name]
except:
result = _str_exc()
return "%s: %r" % (name, result)
_infoX(f(x) for x in dummyVars)
except:
_info_exc()
tag = "os.environ.get(XXX)"
try:
def f(name):
return "%s: %r" % (name, os.environ.get(name))
_infoX(f(x) for x in dummyVars)
except:
_info_exc()
tag = "os.getenv(XXX)"
try:
def f(name):
return "%s: %r" % (name, os.getenv(name))
_infoX(f(x) for x in dummyVars)
except:
_info_exc()
name = dummyVars[0]
value = "foo"
tag = "os.putenv(%s) to %r" % (name, value)
try:
results = []
_collect(results, "before", name, t)
os.putenv(name, value)
_collect(results, "after", name, t)
_infoX(results)
except:
_info_exc()
name = dummyVars[1]
value = "bar"
tag = "os.environ[%s] to %r" % (name, value)
try:
results = []
_collect(results, "before", name, t)
os.environ[name] = value
_collect(results, "after", name, t)
_infoX(results)
except:
_info_exc()
name = dummyVars[1]
value = "baz"
tag = "os.putenv(%s) to %r" % (name, value)
try:
results = []
_collect(results, "before", name, t)
os.putenv(name, value)
_collect(results, "after", name, t)
_infoX(results)
except:
_info_exc()
name = dummyVars[1]
value = ""
tag = "os.putenv(%s) to %r" % (name, value)
try:
results = []
_collect(results, "before", name, t)
os.putenv(name, value)
_collect(results, "after", name, t)
_infoX(results)
except:
_info_exc()
name = dummyVars[2]
value = "foo"
tag = "os.unsetenv(%s) from %r" % (name, value)
try:
results = []
os.environ[name] = value
_collect(results, "before", name, t)
os.unsetenv(name)
_collect(results, "after", name, t)
_infoX(results)
except:
_info_exc()
name = dummyVars[2]
value = "foo"
tag = "del os.environ[%s] from %r" % (name, value)
try:
results = []
os.environ[name] = value
_collect(results, "before", name, t)
del os.environ[name]
_collect(results, "after", name, t)
_infoX(results)
except:
_info_exc()
name = dummyVars[2]
value = "foo"
tag = "os.environ.pop(%s) from %r" % (name, value)
try:
results = []
os.environ[name] = value
_collect(results, "before", name, t)
os.environ.pop(name)
_collect(results, "after", name, t)
_infoX(results)
except:
_info_exc()
name = dummyVars[2]
value1 = "foo"
value2 = ""
tag = "os.environ[%s] to %r from %r" % (name, value2, value1)
try:
results = []
os.environ[name] = value1
_collect(results, "before", name, t)
os.environ[name] = value2
_collect(results, "after", name, t)
_infoX(results)
except:
_info_exc()
name = dummyVars[3]
value = '""'
tag = "os.environ[%s] to %r" % (name, value)
try:
results = []
_collect(results, "before", name, t)
os.environ[name] = value
_collect(results, "after", name, t)
_infoX(results)
except:
_info_exc()
def _getExternalValues(t, *args):
t.run_build_system(["---var-name=%s" % x for x in args])
result = dict()
for x in args:
m = re.search(r"^\*\*\*ENV\*\*\* %s: '(.*)' \*\*\*$" % x, t.stdout(),
re.MULTILINE)
if m:
result[x] = m.group(1)
else:
result[x] = None
return result
def _getJamVersionInfo(t):
result = []
# JAM version variables.
t.run_build_system(["---version"])
for m in re.finditer(r"^\*\*\*VAR\*\*\* ([^:]*): (.*)\*\*\*$", t.stdout(),
re.MULTILINE):
name = m.group(1)
value = m.group(2)
if not value:
value = []
elif value[-1] == ' ':
value = value[:-1].split(varSeparator)
else:
value = "!!!INVALID!!! - '%s'" % value
result.append("%s = %s" % (name, value))
result.append("")
# bjam -v output.
t.run_build_system(["-v"])
result.append("--- output for 'bjam -v' ---")
result.append(t.stdout())
# bjam --version output.
t.run_build_system(["--version"], status=1)
result.append("--- output for 'bjam --version' ---")
result.append(t.stdout())
return result
def _init():
toolsetName = "__myDummyToolset__"
t = BoostBuild.Tester(["toolset=%s" % toolsetName], pass_toolset=False,
use_test_config=False)
# Prepare a dummy toolset so we do not get errors in case the default one
# is not found.
t.write(toolsetName + ".jam", """\
import feature ;
feature.extend toolset : %s ;
rule init ( ) { }
""" % toolsetName )
# Python version of the same dummy toolset.
t.write(toolsetName + ".py", """\
from b2.build import feature
feature.extend('toolset', ['%s'])
def init(): pass
""" % toolsetName )
t.write("jamroot.jam", """\
import os ;
.argv = [ modules.peek : ARGV ] ;
local names = [ MATCH ^---var-name=(.*) : $(.argv) ] ;
for x in $(names)
{
value = [ os.environ $(x) ] ;
ECHO ***ENV*** $(x): '$(value)' *** ;
}
if ---version in $(.argv)
{
for x in JAMVERSION JAM_VERSION JAMUNAME JAM_TIMESTAMP_RESOLUTION OS
{
v = [ modules.peek : $(x) ] ;
ECHO ***VAR*** $(x): "$(v:J=%s)" *** ;
}
}
""" % varSeparator)
return t
def _info(*values):
values = list(values) + [""]
BoostBuild.annotation(tag, "\n".join(str(x) for x in values))
def _infoX(values):
_info(*values)
def _info_exc():
_info(_str_exc())
def _str_exc():
exc_type, exc_value = sys.exc_info()[0:2]
if exc_type is None:
exc_type_name = "None"
else:
exc_type_name = exc_type.__name__
return "*** EXCEPTION *** %s - %s ***" % (exc_type_name, exc_value)
###############################################################################
#
# main()
# ------
#
###############################################################################
collectDebugInfo()

View File

@@ -0,0 +1,166 @@
#!/usr/bin/python
import BoostBuild
def test_basic():
'''Tests that feature=value works'''
t = BoostBuild.Tester()
t.write('Jamroot.jam', '''
import feature : feature ;
import toolset : flags ;
feature f1 : 1 2 ;
make output.txt : : @run ;
flags run OPTIONS <f1> ;
actions run { echo $(OPTIONS) > $(<) }
''')
t.run_build_system(['f1=2'])
t.expect_content("bin/*/output.txt", "2")
t.cleanup()
def test_implicit():
'''Tests that implicit features can be named without a feature'''
t = BoostBuild.Tester()
t.write('Jamroot.jam', '''
import feature : feature ;
import toolset : flags ;
feature f1 : v1 v2 : implicit ;
make output.txt : : @run ;
flags run OPTIONS <f1> ;
actions run { echo $(OPTIONS) > $(<) }
''')
t.run_build_system(['v2'])
t.expect_content("bin/*/output.txt", "v2")
t.cleanup()
def test_optional():
'''Tests that feature= works for optional features'''
t = BoostBuild.Tester()
t.write('Jamroot.jam', '''
import feature : feature ;
import toolset : flags ;
feature f1 : 1 2 : optional ;
make output.txt : : @run ;
flags run OPTIONS <f1> ;
actions run { echo b $(OPTIONS) > $(<) }
''')
t.run_build_system(['f1='])
t.expect_content("bin/*/output.txt", "b")
t.cleanup()
def test_free():
'''Free features named on the command line apply to all targets
everywhere. Free features can contain any characters, even those
that have a special meaning.'''
t = BoostBuild.Tester()
t.write('Jamroot.jam', '''
import feature : feature ;
import toolset : flags ;
feature f1 : : free ;
make output1.txt : : @run : <dependency>output2.txt ;
make output2.txt : : @run ;
explicit output2.txt ;
flags run OPTIONS <f1> ;
actions run { echo $(OPTIONS) > $(<) }
''')
t.run_build_system(['f1=x,/:-'])
t.expect_content("bin*/output1.txt", "x,/:-")
t.expect_content("bin*/output2.txt", "x,/:-")
t.cleanup()
def test_subfeature():
'''Subfeatures should be expressed as feature=value-subvalue'''
t = BoostBuild.Tester()
t.write('Jamroot.jam', '''
import feature : feature subfeature ;
import toolset : flags ;
feature f1 : 1 2 ;
subfeature f1 2 : sub : x y ;
make output.txt : : @run ;
flags run OPTIONS <f1-2:sub> ;
actions run { echo $(OPTIONS) > $(<) }
''')
t.run_build_system(['f1=2-y'])
t.expect_content("bin/*/output.txt", "y")
t.cleanup()
def test_multiple_values():
'''Multiple values of a feature can be given in a comma-separated list'''
t = BoostBuild.Tester()
t.write('Jamroot.jam', '''
import feature : feature ;
import toolset : flags ;
feature f1 : 1 2 3 ;
make output.txt : : @run ;
flags run OPTIONS <f1> ;
actions run { echo $(OPTIONS) > $(<) }
''')
t.run_build_system(['f1=2,3'])
t.expect_content("bin*/f1-2*/output.txt", "2")
t.expect_content("bin*/f1-3*/output.txt", "3")
t.cleanup()
def test_multiple_properties():
'''Multiple properties can be grouped with /'''
t = BoostBuild.Tester()
t.write('Jamroot.jam', '''
import feature : feature ;
import toolset : flags ;
feature f1 : 1 2 ;
feature f2 : 3 4 ;
make output.txt : : @run ;
flags run OPTIONS <f1> ;
flags run OPTIONS <f2> ;
actions run { echo $(OPTIONS) > $(<) }
''')
t.run_build_system(['f1=2/f2=4'])
t.expect_content("bin/*/output.txt", "2 4")
t.cleanup()
def test_cross_product():
'''If multiple properties are specified on the command line
we expand to every possible maximum set of non-conflicting features.
This test should be run after testing individual components in
isolation.'''
t = BoostBuild.Tester()
t.write('Jamroot.jam', '''
import feature : feature ;
import toolset : flags ;
# Make features symmetric to make the paths easier to distinguish
feature f1 : 11 12 13 14 15 : symmetric ;
feature f2 : 21 22 23 : symmetric ;
feature f3 : v1 v2 v3 v4 : implicit symmetric ;
feature f4 : : free ;
make output.txt : : @run ;
flags run OPTIONS <f1> ;
flags run OPTIONS <f2> ;
flags run OPTIONS <f3> ;
flags run OPTIONS <f4> ;
actions run { echo $(OPTIONS) > $(<) }
''')
t.run_build_system(['f1=12,13/f2=22', 'v2', 'v3', 'f1=14', 'f2=23',
'f4=xxx', 'f4=yyy', 'v4/f1=15/f4=zzz'])
t.expect_content("bin*/v2*/f1-12/f2-22*/output.txt", "12 22 v2 xxx yyy")
t.expect_addition("bin*/v2*/f1-12/f2-22*/output.txt")
t.expect_content("bin*/v2*/f1-13/f2-22*/output.txt", "13 22 v2 xxx yyy")
t.expect_addition("bin*/v2*/f1-13/f2-22*/output.txt")
t.expect_content("bin*/v2*/f1-14/f2-23*/output.txt", "14 23 v2 xxx yyy")
t.expect_addition("bin*/v2*/f1-14/f2-23*/output.txt")
t.expect_content("bin*/v3*/f1-12/f2-22*/output.txt", "12 22 v3 xxx yyy")
t.expect_addition("bin*/v3*/f1-12/f2-22*/output.txt")
t.expect_content("bin*/v3*/f1-13/f2-22*/output.txt", "13 22 v3 xxx yyy")
t.expect_addition("bin*/v3*/f1-13/f2-22*/output.txt")
t.expect_content("bin*/v3*/f1-14/f2-23*/output.txt", "14 23 v3 xxx yyy")
t.expect_addition("bin*/v3*/f1-14/f2-23*/output.txt")
t.expect_content("bin*/v4*/f1-15/f2-23*/output.txt", "15 23 v4 xxx yyy zzz")
t.expect_addition("bin*/v4*/f1-15/f2-23*/output.txt")
t.expect_nothing_more()
t.cleanup()
test_basic()
test_implicit()
test_optional()
test_free()
test_subfeature()
test_multiple_values()
test_multiple_properties()
test_cross_product()

View File

@@ -0,0 +1,25 @@
#!/usr/bin/python
# Copyright 2003 Vladimir Prus
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
# Test that composite properties are handled correctly.
import BoostBuild
t = BoostBuild.Tester(use_test_config=False)
t.write("jamroot.jam", """
exe hello : hello.cpp : <variant>release ;
""")
t.write("hello.cpp", """
int main() {}
""")
t.run_build_system()
t.expect_addition("bin/$toolset/release*/hello.exe")
t.cleanup()

View File

@@ -0,0 +1,48 @@
#!/usr/bin/python
# Copyright 2003 Dave Abrahams
# Copyright 2002, 2003, 2004 Vladimir Prus
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
# Test conditional properties.
import BoostBuild
t = BoostBuild.Tester(use_test_config=False)
# Arrange a project which will build only if 'a.cpp' is compiled with "STATIC"
# define.
t.write("a.cpp", """\
#ifdef STATIC
int main() {}
#endif
""")
# Test conditionals in target requirements.
t.write("jamroot.jam", "exe a : a.cpp : <link>static:<define>STATIC ;")
t.run_build_system(["link=static"])
t.expect_addition("bin/$toolset/debug/link-static*/a.exe")
t.rm("bin")
# Test conditionals in project requirements.
t.write("jamroot.jam", """
project : requirements <link>static:<define>STATIC ;
exe a : a.cpp ;
""")
t.run_build_system(["link=static"])
t.expect_addition("bin/$toolset/debug/link-static*/a.exe")
t.rm("bin")
# Regression test for a bug found by Ali Azarbayejani. Conditionals inside
# usage requirement were not being evaluated.
t.write("jamroot.jam", """
lib l : l.cpp : : : <link>static:<define>STATIC ;
exe a : a.cpp l ;
""")
t.write("l.cpp", "int i;")
t.run_build_system(["link=static"])
t.expect_addition("bin/$toolset/debug/link-static*/a.exe")
t.cleanup()

View File

@@ -0,0 +1,43 @@
#!/usr/bin/python
# Copyright 2003 Vladimir Prus
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
# Regression test: it was possible that due to evaluation of conditional
# requirements, two different values of non-free features were present in a
# property set.
import BoostBuild
t = BoostBuild.Tester()
t.write("a.cpp", "")
t.write("jamroot.jam", """
import feature ;
import common ;
feature.feature the_feature : false true : propagated ;
rule maker ( targets * : sources * : properties * )
{
if <the_feature>false in $(properties) &&
<the_feature>true in $(properties)
{
EXIT "Oops, two different values of non-free feature" ;
}
CMD on $(targets) = [ common.file-creation-command ] ;
}
actions maker
{
$(CMD) $(<) ;
}
make a : a.cpp : maker : <variant>debug:<the_feature>true ;
""")
t.run_build_system()
t.cleanup()

View File

@@ -0,0 +1,30 @@
#!/usr/bin/python
# Copyright 2003 Vladimir Prus
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
# Test that conditional properties work, even if property is free, and value
# includes a colon.
import BoostBuild
t = BoostBuild.Tester(use_test_config=False)
t.write("jamroot.jam", """
exe hello : hello.cpp : <variant>debug:<define>"CLASS=Foo::Bar" ;
""")
t.write("hello.cpp", """
namespace Foo { class Bar { } ; }
int main()
{
CLASS c;
c; // Disables the unused variable warning.
}
""")
t.run_build_system(stdout=None, stderr=None)
t.expect_addition("bin/$toolset/debug*/hello.exe")
t.cleanup()

View File

@@ -0,0 +1,45 @@
#!/usr/bin/python
# Copyright 2021 Dmitry Arkhipov (grisumbras@gmail.com)
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
# Test conditionals returning conditionals
import BoostBuild
t = BoostBuild.Tester()
t.write("a.cpp", "")
t.write("jamroot.jam", """
import feature ;
import common ;
feature.feature the_feature : false true : propagated ;
rule add-feature ( properties * )
{
return <the_feature>true ;
}
rule maker ( targets * : sources * : properties * )
{
if ! <the_feature>true in $(properties)
{
EXIT "Need <the_feature>true to build" ;
}
CMD on $(targets) = [ common.file-creation-command ] ;
}
actions maker
{
$(CMD) $(<) ;
}
make a : a.cpp : maker : <variant>debug:<conditional>@add-feature ;
""")
t.run_build_system()
t.cleanup()

View File

@@ -0,0 +1,312 @@
#!/usr/bin/python
# Copyright 2008 Jurko Gospodnetic
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
# Tests that properties conditioned on more than one other property work as
# expected.
import BoostBuild
###############################################################################
#
# test_multiple_conditions()
# --------------------------
#
###############################################################################
def test_multiple_conditions():
"""Basic tests for properties conditioned on multiple other properties."""
t = BoostBuild.Tester(["--user-config=", "--ignore-site-config",
"toolset=testToolset"], pass_toolset=False, use_test_config=False)
t.write("testToolset.jam", """\
import feature ;
feature.extend toolset : testToolset ;
rule init ( ) { }
""")
t.write("testToolset.py", """\
from b2.build import feature
feature.extend('toolset', ["testToolset"])
def init ( ): pass
""")
t.write("jamroot.jam", """\
import feature ;
import notfile ;
import toolset ;
feature.feature description : : free incidental ;
feature.feature aaa : 1 0 : incidental ;
feature.feature bbb : 1 0 : incidental ;
feature.feature ccc : 1 0 : incidental ;
rule buildRule ( name : targets ? : properties * )
{
for local description in [ feature.get-values description : $(properties) ]
{
ECHO "description:" /$(description)/ ;
}
}
notfile testTarget1 : @buildRule : :
<description>d
<aaa>0:<description>a0
<aaa>1:<description>a1
<aaa>0,<bbb>0:<description>a0-b0
<aaa>0,<bbb>1:<description>a0-b1
<aaa>1,<bbb>0:<description>a1-b0
<aaa>1,<bbb>1:<description>a1-b1
<aaa>0,<bbb>0,<ccc>0:<description>a0-b0-c0
<aaa>0,<bbb>0,<ccc>1:<description>a0-b0-c1
<aaa>0,<bbb>1,<ccc>1:<description>a0-b1-c1
<aaa>1,<bbb>0,<ccc>1:<description>a1-b0-c1
<aaa>1,<bbb>1,<ccc>0:<description>a1-b1-c0
<aaa>1,<bbb>1,<ccc>1:<description>a1-b1-c1 ;
""")
t.run_build_system(["aaa=1", "bbb=1", "ccc=1"])
t.expect_output_lines("description: /d/" )
t.expect_output_lines("description: /a0/" , False)
t.expect_output_lines("description: /a1/" )
t.expect_output_lines("description: /a0-b0/" , False)
t.expect_output_lines("description: /a0-b1/" , False)
t.expect_output_lines("description: /a1-b0/" , False)
t.expect_output_lines("description: /a1-b1/" )
t.expect_output_lines("description: /a0-b0-c0/", False)
t.expect_output_lines("description: /a0-b0-c1/", False)
t.expect_output_lines("description: /a0-b1-c1/", False)
t.expect_output_lines("description: /a1-b0-c1/", False)
t.expect_output_lines("description: /a1-b1-c0/", False)
t.expect_output_lines("description: /a1-b1-c1/" )
t.run_build_system(["aaa=0", "bbb=0", "ccc=1"])
t.expect_output_lines("description: /d/" )
t.expect_output_lines("description: /a0/" )
t.expect_output_lines("description: /a1/" , False)
t.expect_output_lines("description: /a0-b0/" )
t.expect_output_lines("description: /a0-b1/" , False)
t.expect_output_lines("description: /a1-b0/" , False)
t.expect_output_lines("description: /a1-b1/" , False)
t.expect_output_lines("description: /a0-b0-c0/", False)
t.expect_output_lines("description: /a0-b0-c1/" )
t.expect_output_lines("description: /a0-b1-c1/", False)
t.expect_output_lines("description: /a1-b0-c1/", False)
t.expect_output_lines("description: /a1-b1-c0/", False)
t.expect_output_lines("description: /a1-b1-c1/", False)
t.run_build_system(["aaa=0", "bbb=0", "ccc=0"])
t.expect_output_lines("description: /d/" )
t.expect_output_lines("description: /a0/" )
t.expect_output_lines("description: /a1/" , False)
t.expect_output_lines("description: /a0-b0/" )
t.expect_output_lines("description: /a0-b1/" , False)
t.expect_output_lines("description: /a1-b0/" , False)
t.expect_output_lines("description: /a1-b1/" , False)
t.expect_output_lines("description: /a0-b0-c0/" )
t.expect_output_lines("description: /a0-b0-c1/", False)
t.expect_output_lines("description: /a0-b1-c1/", False)
t.expect_output_lines("description: /a1-b0-c1/", False)
t.expect_output_lines("description: /a1-b1-c0/", False)
t.expect_output_lines("description: /a1-b1-c1/", False)
t.cleanup()
###############################################################################
#
# test_multiple_conditions_with_toolset_version()
# -----------------------------------------------
#
###############################################################################
def test_multiple_conditions_with_toolset_version():
"""
Regression tests for properties conditioned on the toolset version
subfeature and some additional properties.
"""
toolset = "testToolset" ;
t = BoostBuild.Tester(["--user-config=", "--ignore-site-config"],
pass_toolset=False, use_test_config=False)
t.write(toolset + ".jam", """\
import feature ;
feature.extend toolset : %(toolset)s ;
feature.subfeature toolset %(toolset)s : version : 0 1 ;
rule init ( version ? ) { }
""" % {"toolset": toolset})
t.write("testToolset.py", """\
from b2.build import feature
feature.extend('toolset', ["%(toolset)s"])
feature.subfeature('toolset', "%(toolset)s", "version", ['0','1'])
def init (version=''): pass
""" % {"toolset": toolset})
t.write("jamroot.jam", """\
import feature ;
import notfile ;
import toolset ;
toolset.using testToolset ;
feature.feature description : : free incidental ;
feature.feature aaa : 0 1 : incidental ;
feature.feature bbb : 0 1 : incidental ;
feature.feature ccc : 0 1 : incidental ;
rule buildRule ( name : targets ? : properties * )
{
local ttt = [ feature.get-values toolset : $(properties) ] ;
local vvv = [ feature.get-values "toolset-testToolset:version" : $(properties) ] ;
local aaa = [ feature.get-values aaa : $(properties) ] ;
local bbb = [ feature.get-values bbb : $(properties) ] ;
local ccc = [ feature.get-values ccc : $(properties) ] ;
ECHO "toolset:" /$(ttt)/ "version:" /$(vvv)/ "aaa/bbb/ccc:" /$(aaa)/$(bbb)/$(ccc)/ ;
for local description in [ feature.get-values description : $(properties) ]
{
ECHO "description:" /$(description)/ ;
}
}
notfile testTarget1 : @buildRule : :
<toolset>testToolset,<aaa>0:<description>t-a0
<toolset>testToolset,<aaa>1:<description>t-a1
<toolset>testToolset-0,<aaa>0:<description>t0-a0
<toolset>testToolset-0,<aaa>1:<description>t0-a1
<toolset>testToolset-1,<aaa>0:<description>t1-a0
<toolset>testToolset-1,<aaa>1:<description>t1-a1
<toolset>testToolset,<aaa>0,<bbb>0:<description>t-a0-b0
<toolset>testToolset,<aaa>0,<bbb>1:<description>t-a0-b1
<toolset>testToolset,<aaa>1,<bbb>0:<description>t-a1-b0
<toolset>testToolset,<aaa>1,<bbb>1:<description>t-a1-b1
<aaa>0,<toolset>testToolset,<bbb>0:<description>a0-t-b0
<aaa>0,<toolset>testToolset,<bbb>1:<description>a0-t-b1
<aaa>1,<toolset>testToolset,<bbb>0:<description>a1-t-b0
<aaa>1,<toolset>testToolset,<bbb>1:<description>a1-t-b1
<aaa>0,<bbb>0,<toolset>testToolset:<description>a0-b0-t
<aaa>0,<bbb>1,<toolset>testToolset:<description>a0-b1-t
<aaa>1,<bbb>0,<toolset>testToolset:<description>a1-b0-t
<aaa>1,<bbb>1,<toolset>testToolset:<description>a1-b1-t
<toolset>testToolset-0,<aaa>0,<bbb>0:<description>t0-a0-b0
<toolset>testToolset-0,<aaa>0,<bbb>1:<description>t0-a0-b1
<toolset>testToolset-0,<aaa>1,<bbb>0:<description>t0-a1-b0
<toolset>testToolset-0,<aaa>1,<bbb>1:<description>t0-a1-b1
<toolset>testToolset-1,<aaa>0,<bbb>0:<description>t1-a0-b0
<toolset>testToolset-1,<aaa>0,<bbb>1:<description>t1-a0-b1
<toolset>testToolset-1,<aaa>1,<bbb>0:<description>t1-a1-b0
<toolset>testToolset-1,<aaa>1,<bbb>1:<description>t1-a1-b1
<aaa>0,<toolset>testToolset-1,<bbb>0:<description>a0-t1-b0
<aaa>0,<toolset>testToolset-1,<bbb>1:<description>a0-t1-b1
<aaa>1,<toolset>testToolset-0,<bbb>0:<description>a1-t0-b0
<aaa>1,<toolset>testToolset-0,<bbb>1:<description>a1-t0-b1
<bbb>0,<aaa>1,<toolset>testToolset-0:<description>b0-a1-t0
<bbb>0,<aaa>0,<toolset>testToolset-1:<description>b0-a0-t1
<bbb>0,<aaa>1,<toolset>testToolset-1:<description>b0-a1-t1
<bbb>1,<aaa>0,<toolset>testToolset-1:<description>b1-a0-t1
<bbb>1,<aaa>1,<toolset>testToolset-0:<description>b1-a1-t0
<bbb>1,<aaa>1,<toolset>testToolset-1:<description>b1-a1-t1 ;
""")
t.run_build_system(["aaa=1", "bbb=1", "ccc=1", "toolset=%s-0" % toolset])
t.expect_output_lines("description: /t-a0/" , False)
t.expect_output_lines("description: /t-a1/" )
t.expect_output_lines("description: /t0-a0/" , False)
t.expect_output_lines("description: /t0-a1/" )
t.expect_output_lines("description: /t1-a0/" , False)
t.expect_output_lines("description: /t1-a1/" , False)
t.expect_output_lines("description: /t-a0-b0/" , False)
t.expect_output_lines("description: /t-a0-b1/" , False)
t.expect_output_lines("description: /t-a1-b0/" , False)
t.expect_output_lines("description: /t-a1-b1/" )
t.expect_output_lines("description: /a0-t-b0/" , False)
t.expect_output_lines("description: /a0-t-b1/" , False)
t.expect_output_lines("description: /a1-t-b0/" , False)
t.expect_output_lines("description: /a1-t-b1/" )
t.expect_output_lines("description: /a0-b0-t/" , False)
t.expect_output_lines("description: /a0-b1-t/" , False)
t.expect_output_lines("description: /a1-b0-t/" , False)
t.expect_output_lines("description: /a1-b1-t/" )
t.expect_output_lines("description: /t0-a0-b0/", False)
t.expect_output_lines("description: /t0-a0-b1/", False)
t.expect_output_lines("description: /t0-a1-b0/", False)
t.expect_output_lines("description: /t0-a1-b1/" )
t.expect_output_lines("description: /t1-a0-b0/", False)
t.expect_output_lines("description: /t1-a0-b1/", False)
t.expect_output_lines("description: /t1-a1-b0/", False)
t.expect_output_lines("description: /t1-a1-b1/", False)
t.expect_output_lines("description: /a0-t1-b0/", False)
t.expect_output_lines("description: /a0-t1-b1/", False)
t.expect_output_lines("description: /a1-t0-b0/", False)
t.expect_output_lines("description: /a1-t0-b1/" )
t.expect_output_lines("description: /b0-a1-t0/", False)
t.expect_output_lines("description: /b0-a0-t1/", False)
t.expect_output_lines("description: /b0-a1-t1/", False)
t.expect_output_lines("description: /b1-a0-t1/", False)
t.expect_output_lines("description: /b1-a1-t0/" )
t.expect_output_lines("description: /b1-a1-t1/", False)
t.run_build_system(["aaa=1", "bbb=1", "ccc=1", "toolset=%s-1" % toolset])
t.expect_output_lines("description: /t-a0/" , False)
t.expect_output_lines("description: /t-a1/" )
t.expect_output_lines("description: /t0-a0/" , False)
t.expect_output_lines("description: /t0-a1/" , False)
t.expect_output_lines("description: /t1-a0/" , False)
t.expect_output_lines("description: /t1-a1/" )
t.expect_output_lines("description: /t-a0-b0/" , False)
t.expect_output_lines("description: /t-a0-b1/" , False)
t.expect_output_lines("description: /t-a1-b0/" , False)
t.expect_output_lines("description: /t-a1-b1/" )
t.expect_output_lines("description: /a0-t-b0/" , False)
t.expect_output_lines("description: /a0-t-b1/" , False)
t.expect_output_lines("description: /a1-t-b0/" , False)
t.expect_output_lines("description: /a1-t-b1/" )
t.expect_output_lines("description: /a0-b0-t/" , False)
t.expect_output_lines("description: /a0-b1-t/" , False)
t.expect_output_lines("description: /a1-b0-t/" , False)
t.expect_output_lines("description: /a1-b1-t/" )
t.expect_output_lines("description: /t0-a0-b0/", False)
t.expect_output_lines("description: /t0-a0-b1/", False)
t.expect_output_lines("description: /t0-a1-b0/", False)
t.expect_output_lines("description: /t0-a1-b1/", False)
t.expect_output_lines("description: /t1-a0-b0/", False)
t.expect_output_lines("description: /t1-a0-b1/", False)
t.expect_output_lines("description: /t1-a1-b0/", False)
t.expect_output_lines("description: /t1-a1-b1/" )
t.expect_output_lines("description: /a0-t1-b0/", False)
t.expect_output_lines("description: /a0-t1-b1/", False)
t.expect_output_lines("description: /a1-t0-b0/", False)
t.expect_output_lines("description: /a1-t0-b1/", False)
t.expect_output_lines("description: /b0-a1-t0/", False)
t.expect_output_lines("description: /b0-a0-t1/", False)
t.expect_output_lines("description: /b0-a1-t1/", False)
t.expect_output_lines("description: /b1-a0-t1/", False)
t.expect_output_lines("description: /b1-a1-t0/", False)
t.expect_output_lines("description: /b1-a1-t1/" )
t.cleanup()
###############################################################################
#
# main()
# ------
#
###############################################################################
test_multiple_conditions()
test_multiple_conditions_with_toolset_version()

View File

@@ -0,0 +1,397 @@
#!/usr/bin/python
# Copyright 2008, 2012 Jurko Gospodnetic
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
# Test Boost Build configuration file handling.
import BoostBuild
import TestCmd
import os
import os.path
import re
###############################################################################
#
# test_user_configuration()
# -------------------------
#
###############################################################################
def test_user_configuration():
"""
Test Boost Build user configuration handling. Both relative and absolute
path handling is tested.
"""
implicitConfigLoadMessage = \
"notice: Loading user-config configuration file: *"
explicitConfigLoadMessage = \
"notice: Loading explicitly specified user configuration file:"
disabledConfigLoadMessage = \
"notice: User configuration file loading explicitly disabled."
testMessage = "_!_!_!_!_!_!_!_!_ %s _!_!_!_!_!_!_!_!_"
toolsetName = "__myDummyToolset__"
subdirName = "ASubDirectory"
configFileNames = ["ups_lala_1.jam", "ups_lala_2.jam",
os.path.join(subdirName, "ups_lala_3.jam")]
t = BoostBuild.Tester(["toolset=%s" % toolsetName,
"--debug-configuration"], pass_toolset=False, use_test_config=False)
for configFileName in configFileNames:
message = "ECHO \"%s\" ;" % testMessage % configFileName
# We need to double any backslashes in the message or Jam will
# interpret them as escape characters.
t.write(configFileName, message.replace("\\", "\\\\"))
# Prepare a dummy toolset so we do not get errors in case the default one
# is not found.
t.write(toolsetName + ".jam", """\
import feature ;
feature.extend toolset : %s ;
rule init ( ) { }
""" % toolsetName)
# Python version of the same dummy toolset.
t.write(toolsetName + ".py", """\
from b2.build import feature
feature.extend('toolset', ['%s'])
def init(): pass
""" % toolsetName)
t.write("jamroot.jam", """\
local test-index = [ MATCH ---test-id---=(.*) : [ modules.peek : ARGV ] ] ;
ECHO test-index: $(test-index:E=(unknown)) ;
""")
class LocalTester:
def __init__(self, tester):
self.__tester = tester
self.__test_ids = []
def __assertionFailure(self, message):
BoostBuild.annotation("failure", "Internal test assertion failure "
"- %s" % message)
self.__tester.fail_test(1)
def __call__(self, test_id, env, extra_args=None, *args, **kwargs):
if env == "" and not canSetEmptyEnvironmentVariable:
self.__assertionFailure("Can not set empty environment "
"variables on this platform.")
self.__registerTestId(str(test_id))
if extra_args is None:
extra_args = []
extra_args.append("---test-id---=%s" % test_id)
env_name = "BOOST_BUILD_USER_CONFIG"
previous_env = os.environ.get(env_name)
_env_set(env_name, env)
try:
self.__tester.run_build_system(extra_args, *args, **kwargs)
finally:
_env_set(env_name, previous_env)
def __registerTestId(self, test_id):
if test_id in self.__test_ids:
self.__assertionFailure("Multiple test cases encountered "
"using the same test id '%s'." % test_id)
self.__test_ids.append(test_id)
test = LocalTester(t)
test(1, None)
t.expect_output_lines(explicitConfigLoadMessage, False)
t.expect_output_lines(disabledConfigLoadMessage, False)
t.expect_output_lines(testMessage % configFileNames[0], False)
t.expect_output_lines(testMessage % configFileNames[1], False)
t.expect_output_lines(testMessage % configFileNames[2], False)
test(2, None, ["--user-config="])
t.expect_output_lines(implicitConfigLoadMessage, False)
t.expect_output_lines(explicitConfigLoadMessage, False)
t.expect_output_lines(disabledConfigLoadMessage)
t.expect_output_lines(testMessage % configFileNames[0], False)
t.expect_output_lines(testMessage % configFileNames[1], False)
t.expect_output_lines(testMessage % configFileNames[2], False)
test(3, None, ['--user-config=""'])
t.expect_output_lines(implicitConfigLoadMessage, False)
t.expect_output_lines(explicitConfigLoadMessage, False)
t.expect_output_lines(disabledConfigLoadMessage)
t.expect_output_lines(testMessage % configFileNames[0], False)
t.expect_output_lines(testMessage % configFileNames[1], False)
t.expect_output_lines(testMessage % configFileNames[2], False)
test(4, None, ['--user-config="%s"' % configFileNames[0]])
t.expect_output_lines(implicitConfigLoadMessage, False)
t.expect_output_lines(explicitConfigLoadMessage)
t.expect_output_lines(disabledConfigLoadMessage, False)
t.expect_output_lines(testMessage % configFileNames[0])
t.expect_output_lines(testMessage % configFileNames[1], False)
t.expect_output_lines(testMessage % configFileNames[2], False)
test(5, None, ['--user-config="%s"' % configFileNames[2]])
t.expect_output_lines(implicitConfigLoadMessage, False)
t.expect_output_lines(explicitConfigLoadMessage)
t.expect_output_lines(disabledConfigLoadMessage, False)
t.expect_output_lines(testMessage % configFileNames[0], False)
t.expect_output_lines(testMessage % configFileNames[1], False)
t.expect_output_lines(testMessage % configFileNames[2])
test(6, None, ['--user-config="%s"' % os.path.abspath(configFileNames[1])])
t.expect_output_lines(implicitConfigLoadMessage, False)
t.expect_output_lines(explicitConfigLoadMessage)
t.expect_output_lines(disabledConfigLoadMessage, False)
t.expect_output_lines(testMessage % configFileNames[0], False)
t.expect_output_lines(testMessage % configFileNames[1])
t.expect_output_lines(testMessage % configFileNames[2], False)
test(7, None, ['--user-config="%s"' % os.path.abspath(configFileNames[2])])
t.expect_output_lines(implicitConfigLoadMessage, False)
t.expect_output_lines(explicitConfigLoadMessage)
t.expect_output_lines(disabledConfigLoadMessage, False)
t.expect_output_lines(testMessage % configFileNames[0], False)
t.expect_output_lines(testMessage % configFileNames[1], False)
t.expect_output_lines(testMessage % configFileNames[2])
if canSetEmptyEnvironmentVariable:
test(8, "")
t.expect_output_lines(implicitConfigLoadMessage, False)
t.expect_output_lines(explicitConfigLoadMessage, False)
t.expect_output_lines(disabledConfigLoadMessage, True)
t.expect_output_lines(testMessage % configFileNames[0], False)
t.expect_output_lines(testMessage % configFileNames[1], False)
t.expect_output_lines(testMessage % configFileNames[2], False)
test(9, '""')
t.expect_output_lines(implicitConfigLoadMessage, False)
t.expect_output_lines(explicitConfigLoadMessage, False)
t.expect_output_lines(disabledConfigLoadMessage)
t.expect_output_lines(testMessage % configFileNames[0], False)
t.expect_output_lines(testMessage % configFileNames[1], False)
t.expect_output_lines(testMessage % configFileNames[2], False)
test(10, configFileNames[1])
t.expect_output_lines(implicitConfigLoadMessage, False)
t.expect_output_lines(explicitConfigLoadMessage)
t.expect_output_lines(disabledConfigLoadMessage, False)
t.expect_output_lines(testMessage % configFileNames[0], False)
t.expect_output_lines(testMessage % configFileNames[1])
t.expect_output_lines(testMessage % configFileNames[2], False)
test(11, configFileNames[1], ['--user-config=""'])
t.expect_output_lines(implicitConfigLoadMessage, False)
t.expect_output_lines(explicitConfigLoadMessage, False)
t.expect_output_lines(disabledConfigLoadMessage)
t.expect_output_lines(testMessage % configFileNames[0], False)
t.expect_output_lines(testMessage % configFileNames[1], False)
t.expect_output_lines(testMessage % configFileNames[2], False)
test(12, configFileNames[1], ['--user-config="%s"' % configFileNames[0]])
t.expect_output_lines(implicitConfigLoadMessage, False)
t.expect_output_lines(explicitConfigLoadMessage)
t.expect_output_lines(disabledConfigLoadMessage, False)
t.expect_output_lines(testMessage % configFileNames[0])
t.expect_output_lines(testMessage % configFileNames[1], False)
t.expect_output_lines(testMessage % configFileNames[2], False)
if canSetEmptyEnvironmentVariable:
test(13, "", ['--user-config="%s"' % configFileNames[0]])
t.expect_output_lines(implicitConfigLoadMessage, False)
t.expect_output_lines(explicitConfigLoadMessage)
t.expect_output_lines(disabledConfigLoadMessage, False)
t.expect_output_lines(testMessage % configFileNames[0])
t.expect_output_lines(testMessage % configFileNames[1], False)
t.expect_output_lines(testMessage % configFileNames[2], False)
test(14, '""', ['--user-config="%s"' % configFileNames[0]])
t.expect_output_lines(implicitConfigLoadMessage, False)
t.expect_output_lines(explicitConfigLoadMessage)
t.expect_output_lines(disabledConfigLoadMessage, False)
t.expect_output_lines(testMessage % configFileNames[0])
t.expect_output_lines(testMessage % configFileNames[1], False)
t.expect_output_lines(testMessage % configFileNames[2], False)
test(15, "invalid", ['--user-config="%s"' % configFileNames[0]])
t.expect_output_lines(implicitConfigLoadMessage, False)
t.expect_output_lines(explicitConfigLoadMessage)
t.expect_output_lines(disabledConfigLoadMessage, False)
t.expect_output_lines(testMessage % configFileNames[0])
t.expect_output_lines(testMessage % configFileNames[1], False)
t.expect_output_lines(testMessage % configFileNames[2], False)
t.cleanup()
###############################################################################
#
# Private interface.
#
###############################################################################
def _canSetEmptyEnvironmentVariable():
"""
Unfortunately different OSs (and possibly Python implementations as well)
have different interpretations of what it means to set an environment
variable to an empty string. Some (e.g. Windows) interpret it as unsetting
the variable and some (e.g. AIX or Darwin) actually set it to an empty
string.
"""
dummyName = "UGNABUNGA_FOO_BAR_BAZ_FEE_FAE_FOU_FAM"
original = os.environ.get(dummyName)
_env_set(dummyName, "")
result = _getExternalEnv(dummyName) == ""
_env_set(dummyName, original)
return result
def _env_del(name):
"""
Unsets the given environment variable if it is currently set.
Note that we can not use os.environ.pop() or os.environ.clear() here
since prior to Python 2.6 these functions did not remove the actual
environment variable by calling os.unsetenv().
"""
try:
del os.environ[name]
except KeyError:
pass
def _env_set(name, value):
"""
Sets the given environment variable value or unsets it, if the value is
None.
"""
if value is None:
_env_del(name)
else:
os.environ[name] = value
def _getExternalEnv(name):
toolsetName = "__myDummyToolset__"
t = BoostBuild.Tester(["toolset=%s" % toolsetName], pass_toolset=False,
use_test_config=False)
try:
# Prepare a dummy toolset so we do not get errors in case the default
# one is not found.
t.write(toolsetName + ".jam", """\
import feature ;
feature.extend toolset : %s ;
rule init ( ) { }
""" % toolsetName)
# Python version of the same dummy toolset.
t.write(toolsetName + ".py", """\
from b2.build import feature
feature.extend('toolset', ['%s'])
def init(): pass
""" % toolsetName)
t.write("jamroot.jam", """\
import os ;
local names = [ MATCH ^---var-name---=(.*) : [ modules.peek : ARGV ] ] ;
for x in $(names)
{
value = [ os.environ $(x) ] ;
ECHO "###" $(x): '$(value)' "###" ;
}
""")
t.run_build_system(["---var-name---=%s" % name])
m = re.search("^### %s: '(.*)' ###$" % name, t.stdout(), re.MULTILINE)
if m:
return m.group(1)
finally:
t.cleanup()
def test_site_config():
# Ignore user-config, just in case it depends on the user's site-config.jam
t = BoostBuild.Tester(["--user-config="], use_test_config=False,
pass_toolset=0)
# We can immediately exit after we finish loading the config files
t.write("Jamroot", "EXIT Done : 0 ;")
t.write("my-site-config.jam", "ECHO Loaded my-site-config ;")
t.run_build_system(["--site-config=my-site-config.jam"],
stdout="Loaded my-site-config\nDone\n")
t.run_build_system(["--ignore-site-config", "--debug-configuration"])
t.expect_output_lines("""\
notice: Site configuration files will be ignored due to the
notice: --ignore-site-config command-line option.""")
t.run_build_system(["--site-config=", "--debug-configuration"])
t.expect_output_lines("""\
notice: Site configuration file loading explicitly disabled.""")
t.cleanup()
def test_global_config():
t = BoostBuild.Tester(use_test_config=False, pass_toolset=0)
t.write("my-config.jam", "ECHO Loading my-config ;")
t.write("Jamroot", "EXIT Done : 0 ;")
t.write("project-config.jam", "ECHO bad ;")
t.run_build_system(["--config=my-config.jam", "--debug-configuration"],
match=TestCmd.match_re, stdout=
r"""notice: found boost-build\.jam at .*
notice: loading B2 from .*
notice: Searching '.*' for all-config configuration file 'my-config\.jam'\.
notice: Loading all-config configuration file 'my-config\.jam' from '.*'\.
Loading my-config
notice: Regular configuration files will be ignored due
notice: to the global configuration being loaded\.
Done
""")
t.run_build_system(["--config=", "--debug-configuration"],
match=TestCmd.match_re, stdout=
r"""notice: found boost-build\.jam at .*
notice: loading B2 from .*
notice: Configuration file loading explicitly disabled.
Done
""")
t.cleanup()
def test_project_config():
t = BoostBuild.Tester(["--user-config=", "--site-config="],
use_test_config=False, pass_toolset=False)
t.write("Jamroot", "EXIT Done : 0 ;")
t.write("project-config.jam", "ECHO Loading Root ;")
t.write("my-project-config.jam", "ECHO Loading explicit ;")
t.write("sub/project-config.jam", "ECHO Loading subdir ;")
t.write("sub/Jamfile", "")
t.run_build_system(stdout="Loading Root\nDone\n")
t.run_build_system(subdir="sub", stdout="Loading subdir\nDone\n")
t.rm("sub/project-config.jam")
t.run_build_system(subdir="sub", stdout="Loading Root\nDone\n")
t.run_build_system(["--project-config=my-project-config.jam"],
stdout="Loading explicit\nDone\n")
t.cleanup()
###############################################################################
#
# main()
# ------
#
###############################################################################
canSetEmptyEnvironmentVariable = _canSetEmptyEnvironmentVariable()
test_user_configuration()
test_site_config()
test_global_config()
test_project_config()

View File

@@ -0,0 +1,267 @@
#!/usr/bin/python
# Copyright 2017 Steven Watanabe
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
# Tests configure.check-target-builds and friends
import BoostBuild
def test_check_target_builds():
t = BoostBuild.Tester(use_test_config=0)
t.write("Jamroot", """
import configure ;
obj pass : pass.cpp ;
obj fail : fail.cpp ;
explicit pass fail ;
obj foo : foo.cpp :
[ configure.check-target-builds pass : <define>PASS : <define>FAIL ] ;
obj bar : foo.cpp :
[ configure.check-target-builds fail : <define>FAIL : <define>PASS ] ;
""")
t.write("pass.cpp", "void f() {}\n")
t.write("fail.cpp", "#error fail.cpp\n")
t.write("foo.cpp", """
#ifndef PASS
#error PASS not defined
#endif
#ifdef FAIL
#error FAIL is defined
#endif
""")
t.run_build_system()
t.expect_output_lines([
" - pass builds : yes*",
" - fail builds : no*"])
t.expect_addition("bin/$toolset/debug*/pass.obj")
t.expect_addition("bin/$toolset/debug*/foo.obj")
t.expect_addition("bin/$toolset/debug*/bar.obj")
t.expect_nothing_more()
# An up-to-date build should use the cache
t.run_build_system()
t.expect_output_lines([
" - pass builds : yes (cached)*",
" - fail builds : no (cached)*"])
t.expect_nothing_more()
# -a should re-run everything, including configuration checks
t.run_build_system(["-a"])
t.expect_output_lines([
" - pass builds : yes*",
" - fail builds : no*"])
t.expect_touch("bin/$toolset/debug*/pass.obj")
t.expect_touch("bin/$toolset/debug*/foo.obj")
t.expect_touch("bin/$toolset/debug*/bar.obj")
t.expect_nothing_more()
# --reconfigure should re-run configuration checks only
t.run_build_system(["--reconfigure"])
t.expect_output_lines([
" - pass builds : yes*",
" - fail builds : no*"])
t.expect_touch("bin/$toolset/debug*/pass.obj")
t.expect_nothing_more()
# -a -n should not rebuild configuration checks
t.run_build_system(["-a", "-n"])
t.expect_output_lines([
" - pass builds : yes (cached)*",
" - fail builds : no (cached)*"])
t.expect_nothing_more()
# --clean-all should clear all configuration checks
t.run_build_system(["--clean-all"])
t.expect_output_lines([
" - pass builds : yes (cached)*",
" - fail builds : no (cached)*"])
t.expect_removal("bin/$toolset/debug*/pass.obj")
t.expect_removal("bin/$toolset/debug*/foo.obj")
t.expect_removal("bin/$toolset/debug*/bar.obj")
t.expect_nothing_more()
# If configuration checks are absent, then --clean-all
# should create them and then delete them again. This
# currently fails because clean cannot remove targets
# that were created in the same build.
#t.run_build_system(["--clean-all"])
#t.expect_output_lines([
# " - pass builds : yes",
# " - fail builds : no"])
#t.expect_nothing_more()
# Just verify that we're actually in the initial
# state here.
t.run_build_system()
t.expect_output_lines([
" - pass builds : yes*",
" - fail builds : no*"])
t.expect_addition("bin/$toolset/debug*/pass.obj")
t.expect_addition("bin/$toolset/debug*/foo.obj")
t.expect_addition("bin/$toolset/debug*/bar.obj")
t.expect_nothing_more()
t.cleanup()
def test_choose():
t = BoostBuild.Tester(use_test_config=0)
t.write("Jamroot", """
import configure ;
obj pass : pass.cpp ;
obj fail : fail.cpp ;
explicit pass fail ;
obj foo : foo.cpp :
[ configure.choose "which one?" : fail <define>FAIL : pass <define>PASS ] ;
""")
t.write("pass.cpp", "void f() {}\n")
t.write("fail.cpp", "#error fail.cpp\n")
t.write("foo.cpp", """
#ifndef PASS
#error PASS not defined
#endif
#ifdef FAIL
#error FAIL is defined
#endif
""")
t.run_build_system()
t.expect_output_lines([
" - which one? : pass*"])
t.expect_addition("bin/$toolset/debug*/pass.obj")
t.expect_addition("bin/$toolset/debug*/foo.obj")
t.expect_nothing_more()
# An up-to-date build should use the cache
t.run_build_system()
t.expect_output_lines([
" - which one? : pass (cached)*"])
t.expect_nothing_more()
# -a should re-run everything, including configuration checks
t.run_build_system(["-a"])
t.expect_output_lines([
" - which one? : pass*"])
t.expect_touch("bin/$toolset/debug*/pass.obj")
t.expect_touch("bin/$toolset/debug*/foo.obj")
t.expect_nothing_more()
# --reconfigure should re-run configuration checks only
t.run_build_system(["--reconfigure"])
t.expect_output_lines([
" - which one? : pass*"])
t.expect_touch("bin/$toolset/debug*/pass.obj")
t.expect_nothing_more()
# -a -n should not rebuild configuration checks
t.run_build_system(["-a", "-n"])
t.expect_output_lines([
" - which one? : pass (cached)*"])
t.expect_nothing_more()
# --clean-all should clear all configuration checks
t.run_build_system(["--clean-all"])
t.expect_output_lines([
" - which one? : pass (cached)*"])
t.expect_removal("bin/$toolset/debug*/pass.obj")
t.expect_removal("bin/$toolset/debug*/foo.obj")
t.expect_nothing_more()
# If configuration checks are absent, then --clean-all
# should create them and then delete them again. This
# currently fails because clean cannot remove targets
# that were created in the same build.
#t.run_build_system(["--clean-all"])
#t.expect_output_lines([
# " - which one? : pass"])
#t.expect_nothing_more()
# Just verify that we're actually in the initial
# state here.
t.run_build_system()
t.expect_output_lines([
" - which one? : pass*"])
t.expect_addition("bin/$toolset/debug*/pass.obj")
t.expect_addition("bin/$toolset/debug*/foo.obj")
t.expect_nothing_more()
t.cleanup()
def test_translation():
"""Tests scoping for targets, paths, and rules within check-target-builds"""
t = BoostBuild.Tester(use_test_config=0)
t.write("Jamroot", "")
t.write("subdir/Jamfile", """
import configure ;
obj pass : pass.cpp ;
obj fail : fail.cpp ;
explicit pass fail ;
obj foo : :
[ configure.check-target-builds pass
: [ configure.check-target-builds fail : <define>FAIL
: <define>PASS <include>include1 <conditional>@c1 ]
: <define>FAIL ] ;
obj bar : :
[ configure.choose "which one?" : pass
[ configure.choose "Try again?" : pass
<define>PASS <include>include1 <conditional>@c1 ] ] ;
rule c1 ( properties * )
{
return <include>include2 <source>foo.cpp ;
}
""")
t.write("subdir/include1/a.h", "")
t.write("subdir/include2/b.h", "")
t.write("subdir/pass.cpp", "void f() {}\n")
t.write("subdir/fail.cpp", "#error fail.cpp\n")
t.write("subdir/foo.cpp", """
#include <a.h>
#include <b.h>
#ifndef PASS
#error PASS not defined
#endif
#ifdef FAIL
#error FAIL is defined
#endif
""")
t.run_build_system(["subdir"])
t.expect_output_lines([
" - pass builds : yes*",
" - fail builds : no*"])
t.expect_addition("subdir/bin/$toolset/debug*/pass.obj")
t.expect_addition("subdir/bin/$toolset/debug*/foo.obj")
t.expect_addition("subdir/bin/$toolset/debug*/bar.obj")
t.expect_nothing_more()
t.cleanup()
def test_choose_none():
"""Tests choose when none of the alternatives match."""
t = BoostBuild.Tester(use_test_config=0)
t.write("Jamroot", """
import configure ;
obj fail : fail.cpp ;
explicit pass fail ;
obj foo : foo.cpp :
[ configure.choose "which one?" : fail <define>FAIL ] ;
""")
t.write("fail.cpp", "#error fail.cpp\n")
t.write("foo.cpp", """
#ifdef FAIL
#error FAIL is defined
#endif
""")
t.run_build_system()
t.expect_output_lines([
" - which one? : none*"])
# An up-to-date build should use the cache
t.run_build_system()
t.expect_output_lines([
" - which one? : none (cached)*"])
t.expect_nothing_more()
t.cleanup()
test_check_target_builds()
test_choose()
test_translation()
test_choose_none()

View File

@@ -0,0 +1,69 @@
#!/usr/bin/python
#
# Copyright (c) 2008 Steven Watanabe
#
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
# Test that the common.copy rule set the modification date of the new file to
# the current time.
import BoostBuild
tester = BoostBuild.Tester(use_test_config=False)
tester.write("test1.cpp", """\
template<bool, int M, class Next>
struct time_waster {
typedef typename time_waster<true, M-1, time_waster>::type type1;
typedef typename time_waster<false, M-1, time_waster>::type type2;
typedef void type;
};
template<bool B, class Next>
struct time_waster<B, 0, Next> {
typedef void type;
};
typedef time_waster<true, 10, void>::type type;
int f() { return 0; }
""")
tester.write("test2.cpp", """\
template<bool, int M, class Next>
struct time_waster {
typedef typename time_waster<true, M-1, time_waster>::type type1;
typedef typename time_waster<false, M-1, time_waster>::type type2;
typedef void type;
};
template<bool B, class Next>
struct time_waster<B, 0, Next> {
typedef void type;
};
typedef time_waster<true, 10, void>::type type;
int g() { return 0; }
""")
tester.write("jamroot.jam", """\
obj test2 : test2.cpp ;
obj test1 : test1.cpp : <dependency>test2 ;
install test2i : test2 : <dependency>test1 ;
""")
tester.run_build_system()
tester.expect_addition("bin/$toolset/debug*/test2.obj")
tester.expect_addition("bin/$toolset/debug*/test1.obj")
tester.expect_addition("test2i/test2.obj")
tester.expect_nothing_more()
test2src = tester.read("test2i/test2.obj", binary=True)
test2dest = tester.read("bin/$toolset/debug*/test2.obj", binary=True)
if test2src != test2dest:
BoostBuild.annotation("failure", "The object file was not copied "
"correctly")
tester.fail_test(1)
tester.run_build_system(["-d1"])
tester.expect_output_lines("common.copy*", False)
tester.expect_nothing_more()
tester.cleanup()

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,62 @@
#!/usr/bin/python
# Copyright 2012. Jurko Gospodnetic
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
# Test correct "-p" option handling.
import BoostBuild
t = BoostBuild.Tester(["-d1"], pass_toolset=False)
t.write("file.jam", """\
prefix = "echo \\"" ;
suffix = "\\"" ;
if $(NT)
{
prefix = "(echo " ;
suffix = ")" ;
}
actions go
{
$(prefix)stdout$(suffix)
>&2 $(prefix)stderr$(suffix)
}
ECHO "{{{" $(XXX) "}}}" ;
ALWAYS all ;
go all ;
""")
t.run_build_system(["-ffile.jam", "-sXXX=1"], stderr="")
t.expect_output_lines("{{{ 1 }}}")
t.expect_output_lines("stdout")
t.expect_output_lines("stderr")
t.expect_nothing_more()
t.run_build_system(["-ffile.jam", "-sXXX=2", "-p0"], stderr="")
t.expect_output_lines("{{{ 2 }}}")
t.expect_output_lines("stdout")
t.expect_output_lines("stderr")
t.expect_nothing_more()
t.run_build_system(["-ffile.jam", "-sXXX=3", "-p1"], stderr="")
t.expect_output_lines("{{{ 3 }}}")
t.expect_output_lines("stdout")
t.expect_output_lines("stderr*", False)
t.expect_nothing_more()
t.run_build_system(["-ffile.jam", "-sXXX=4", "-p2"], stderr="stderr\n")
t.expect_output_lines("{{{ 4 }}}")
t.expect_output_lines("stdout*", False)
t.expect_output_lines("stderr*", False)
t.expect_nothing_more()
t.run_build_system(["-ffile.jam", "-sXXX=5", "-p3"], stderr="stderr\n")
t.expect_output_lines("{{{ 5 }}}")
t.expect_output_lines("stdout")
t.expect_output_lines("stderr*", False)
t.expect_nothing_more()
t.cleanup()

View File

@@ -0,0 +1,27 @@
#!/usr/bin/python
# Copyright 2007 Rene Rivera.
# Copyright 2011 Steven Watanabe
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
import BoostBuild
t = BoostBuild.Tester(pass_toolset=0)
t.write("file.jam", """\
actions quietly .a. { $(ACTION) }
rule .a.
{
DEPENDS $(<) : $(>) ;
}
NOTFILE subtest ;
.a. subtest_a : subtest ;
DEPENDS all : subtest_a ;
""")
t.run_build_system(["-ffile.jam", "-sACTION=invalid"], status=1)
t.cleanup()

View File

@@ -0,0 +1,61 @@
#!/usr/bin/python
# Copyright 2007 Rene Rivera.
# Copyright 2011 Steven Watanabe
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
import BoostBuild
t = BoostBuild.Tester(pass_toolset=0)
t.write("file.jam", """\
actions quietly .a.
{
echo [$(<:B)] 0
echo [$(<:B)] 1
echo [$(<:B)] 2
}
rule .a.
{
DEPENDS $(<) : $(>) ;
}
NOTFILE subtest ;
.a. subtest_a : subtest ;
.a. subtest_b : subtest ;
DEPENDS all : subtest_a subtest_b ;
""")
t.run_build_system(["-ffile.jam", "-d2"], stdout="""\
...found 4 targets...
...updating 2 targets...
.a. subtest_a
echo [subtest_a] 0
echo [subtest_a] 1
echo [subtest_a] 2
[subtest_a] 0
[subtest_a] 1
[subtest_a] 2
.a. subtest_b
echo [subtest_b] 0
echo [subtest_b] 1
echo [subtest_b] 2
[subtest_b] 0
[subtest_b] 1
[subtest_b] 2
...updated 2 targets...
""")
t.run_build_system(["-ffile.jam", "-d1"], stdout="""\
...found 4 targets...
...updating 2 targets...
...updated 2 targets...
""")
t.cleanup()

View File

@@ -0,0 +1,105 @@
#!/usr/bin/python
# Copyright 2001 Dave Abrahams
# Copyright 2011 Steven Watanabe
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
import BoostBuild
def simple_args(start, finish):
return " : ".join("%d" % x for x in range(start, finish + 1))
def test(t, type, input, output, status=0):
code = ["include echo_args.jam ; echo_%s" % type]
if input: code.append(input)
code.append(";")
t.write("file.jam", " ".join(code))
t.run_build_system(["-ffile.jam"], status=status)
t.expect_output_lines(output)
def test_args(t, *args, **kwargs):
test(t, "args", *args, **kwargs)
def test_varargs(t, *args, **kwargs):
test(t, "varargs", *args, **kwargs)
t = BoostBuild.Tester(pass_toolset=0)
t.write("echo_args.jam", """\
NOCARE all ;
rule echo_args ( a b ? c ? : d + : e * )
{
ECHO a= $(a) b= $(b) c= $(c) ":" d= $(d) ":" e= $(e) ;
}
rule echo_varargs ( a b ? c ? : d + : e * : * )
{
ECHO a= $(a) b= $(b) c= $(c) ":" d= $(d) ":" e= $(e)
": rest= "$(4[1]) $(4[2-])
": "$(5[1]) $(5[2-]) ": "$(6[1]) $(6[2-]) ": "$(7[1]) $(7[2-])
": "$(8[1]) $(8[2-]) ": "$(9[1]) $(9[2-]) ": "$(10[1]) $(10[2-])
": "$(11[1]) $(11[2-]) ": "$(12[1]) $(12[2-]) ": "$(13[1]) $(13[2-])
": "$(14[1]) $(14[2-]) ": "$(15[1]) $(15[2-]) ": "$(16[1]) $(16[2-])
": "$(17[1]) $(17[2-]) ": "$(18[1]) $(18[2-]) ": "$(19[1]) $(19[2-])
": "$(20[1]) $(20[2-]) ": "$(21[1]) $(21[2-]) ": "$(22[1]) $(22[2-])
": "$(23[1]) $(23[2-]) ": "$(24[1]) $(24[2-]) ": "$(25[1]) $(25[2-]) ;
}
""")
test_args(t, "", "* missing argument a", status=1)
test_args(t, "1 2 : 3 : 4 : 5", "* extra argument 5", status=1)
test_args(t, "a b c1 c2 : d", "* extra argument c2", status=1)
# Check modifier '?'
test_args(t, "1 2 3 : 4", "a= 1 b= 2 c= 3 : d= 4 : e=")
test_args(t, "1 2 : 3", "a= 1 b= 2 c= : d= 3 : e=")
test_args(t, "1 2 : 3", "a= 1 b= 2 c= : d= 3 : e=")
test_args(t, "1 : 2", "a= 1 b= c= : d= 2 : e=")
# Check modifier '+'
test_args(t, "1", "* missing argument d", status=1)
test_args(t, "1 : 2 3", "a= 1 b= c= : d= 2 3 : e=")
test_args(t, "1 : 2 3 4", "a= 1 b= c= : d= 2 3 4 : e=")
# Check modifier '*'
test_args(t, "1 : 2 : 3", "a= 1 b= c= : d= 2 : e= 3")
test_args(t, "1 : 2 : 3 4", "a= 1 b= c= : d= 2 : e= 3 4")
test_args(t, "1 : 2 : 3 4 5", "a= 1 b= c= : d= 2 : e= 3 4 5")
# Check varargs
test_varargs(t, "1 : 2 : 3 4 5", "a= 1 b= c= : d= 2 : e= 3 4 5")
test_varargs(t, "1 : 2 : 3 4 5 : 6", "a= 1 b= c= : d= 2 : e= 3 4 5 : rest= 6")
test_varargs(t, "1 : 2 : 3 4 5 : 6 7",
"a= 1 b= c= : d= 2 : e= 3 4 5 : rest= 6 7")
test_varargs(t, "1 : 2 : 3 4 5 : 6 7 : 8",
"a= 1 b= c= : d= 2 : e= 3 4 5 : rest= 6 7 : 8")
test_varargs(t, "1 : 2 : 3 4 5 : 6 7 : 8 : 9",
"a= 1 b= c= : d= 2 : e= 3 4 5 : rest= 6 7 : 8 : 9")
test_varargs(t, "1 : 2 : 3 4 5 : 6 7 : 8 : 9 : 10 : 11 : 12 : 13 : 14 : 15 : "
"16 : 17 : 18 : 19a 19b", "a= 1 b= c= : d= 2 : e= 3 4 5 : rest= 6 7 : 8 : "
"9 : 10 : 11 : 12 : 13 : 14 : 15 : 16 : 17 : 18 : 19a 19b")
test_varargs(t, "1 : 2 : 3 4 5 : 6 7 : 8 : 9 : 10 : 11 : 12 : 13 : 14 : 15 : "
"16 : 17 : 18 : 19a 19b 19c : 20", "a= 1 b= c= : d= 2 : e= 3 4 5 : rest= "
"6 7 : 8 : 9 : 10 : 11 : 12 : 13 : 14 : 15 : 16 : 17 : 18 : 19a 19b 19c : "
"20")
# Check varargs upper limit
expected = "a= 1 b= c= : d= 2 : e= 3 : rest= " + simple_args(4, 19)
test_varargs(t, simple_args(1, 19), expected)
test_varargs(t, simple_args(1, 19) + " 19b 19c 19d", expected + " 19b 19c 19d")
'''FIXME: 19 (=LOL_MAX) args is the limit
test_varargs(t, simple_args(1, 19) + " 19b 19c 19d : 20", expected + " 19b "
"19c 19d")
test_varargs(t, simple_args(1, 20), expected)
test_varargs(t, simple_args(1, 50), expected)
'''
t.cleanup()

View File

@@ -0,0 +1,64 @@
#!/usr/bin/python
# Copyright 2011 Steven Watanabe
# Copyright 2020 Rene Ferdinand Rivera Morell
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
import BoostBuild
t = BoostBuild.Tester(["-ffile.jam"], pass_toolset=0)
t.write("file.jam", """\
name = n1 n2 ;
contents = M1 M2 ;
EXIT "file:" "@(o$(name:J=) .txt:E= test -D$(contents))" : 0 ;
""")
t.run_build_system()
t.expect_output_lines("file: on1n2 .txt")
t.expect_addition("on1n2 .txt")
t.expect_content("on1n2 .txt", " test -DM1 -DM2", True)
t.rm(".")
t.write("file.jam", """\
name = n1 n2 ;
contents = M1 M2 ;
actions run { echo file: "@(o$(name:J=) .txt:E= test -D$(contents))" }
run all ;
""")
t.run_build_system(["-d2"])
t.expect_output_lines(' echo file: "on1n2 .txt" ')
t.expect_addition("on1n2 .txt")
t.expect_content("on1n2 .txt", " test -DM1 -DM2", True)
t.rm(".")
t.write("file.jam", """\
name = n1 n2 ;
contents = M1 M2 ;
file = "@($(STDOUT):E= test -D$(contents)\n)" ;
actions run { $(file) }
run all ;
""")
t.run_build_system(["-d1"])
t.expect_output_lines(" test -DM1 -DM2")
t.rm(".")
t.write("file.jam", """\
name = n1 n2 ;
contents = M1 M2 ;
actions run { @($(STDOUT):E= test -D$(contents)\n) }
run all ;
""")
t.run_build_system(["-d1"])
t.expect_output_lines(" test -DM1 -DM2")
t.cleanup()

View File

@@ -0,0 +1,45 @@
#!/usr/bin/python
# Copyright 2001 Dave Abrahams
# Copyright 2011 Steven Watanabe
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
import BoostBuild
import os
t = BoostBuild.Tester(["-d1"], pass_toolset=0)
t.write("subdir1/file-to-bind", "# This file intentionally left blank")
t.write("file.jam", """\
rule do-nothing ( target : source )
{
DEPENDS $(target) : $(source) ;
}
actions quietly do-nothing { }
# Make a non-file target which depends on a file that exists
NOTFILE fake-target ;
SEARCH on file-to-bind = subdir1 ;
do-nothing fake-target : file-to-bind ;
# Set jam up to call our bind-rule
BINDRULE = bind-rule ;
rule bind-rule ( target : path )
{
ECHO "found:" $(target) at $(path) ;
}
DEPENDS all : fake-target ;
""")
t.run_build_system(["-ffile.jam"], stdout="""\
found: all at all
found: file-to-bind at subdir1%sfile-to-bind
...found 3 targets...
""" % os.sep)
t.cleanup()

View File

@@ -0,0 +1,32 @@
#!/usr/bin/python
# Copyright 2002, 2003 Vladimir Prus
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
# This tests correct handling of "-d1" and "-d2" options.
import BoostBuild
t = BoostBuild.Tester(["-ffile.jam"], pass_toolset=0)
t.write("file.jam", """\
actions a { }
actions quietly b { }
ALWAYS all ;
a all ;
b all ;
""")
t.run_build_system(["-d0"], stdout="")
t.run_build_system(["-d1"])
t.expect_output_lines("a all")
t.expect_output_lines("b all", False)
t.run_build_system(["-d2"])
t.expect_output_lines("a all")
t.expect_output_lines("b all")
t.cleanup()

View File

@@ -0,0 +1,51 @@
#!/usr/bin/python
# Copyright 2003 Dave Abrahams
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
# This tests the facilities for deleting modules.
import BoostBuild
t = BoostBuild.Tester(pass_toolset=0)
t.write("file.jam", """
module foo
{
rule bar { }
var = x y ;
}
DELETE_MODULE foo ;
if [ RULENAMES foo ]
{
EXIT DELETE_MODULE failed to kill foo's rules: [ RULENAMES foo ] ;
}
module foo
{
if $(var)
{
EXIT DELETE_MODULE failed to kill foo's variables ;
}
rule bar { }
var = x y ;
DELETE_MODULE foo ;
if $(var)
{
EXIT internal DELETE_MODULE failed to kill foo's variables ;
}
if [ RULENAMES foo ]
{
EXIT internal DELETE_MODULE failed to kill foo's rules: [ RULENAMES foo ] ;
}
}
DEPENDS all : xx ;
NOTFILE xx ;
""")
t.run_build_system(["-ffile.jam"], status=0)
t.cleanup()

View File

@@ -0,0 +1,157 @@
#!/usr/bin/python
# Copyright 2003 Vladimir Prus
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
# This tests correct handling of dependencies, specifically, on generated
# sources, and from generated sources.
import BoostBuild
import string
t = BoostBuild.Tester(pass_toolset=0)
t.write("core-dependency-helpers", """
rule hdrrule
{
INCLUDES $(1) : $(2) ;
}
actions copy
{
cp $(>) $(<) || copy $(>) $(<)
}
""")
code = """include core-dependency-helpers ;
DEPENDS all : a ;
DEPENDS a : b ;
actions create-b
{
echo "#include <foo.h>" > $(<)
}
copy a : b ;
create-b b ;
HDRRULE on b foo.h bar.h = hdrrule ;
HDRSCAN on b foo.h bar.h = \"#include <(.*)>\" ;
"""
# This creates 'a' which depends on 'b', which is generated. The generated 'b'
# contains '#include <foo.h>' and no rules for foo.h are given. The system
# should error out on the first invocation.
t.run_build_system(["-f-"], status=1, stdin=code)
t.fail_test(t.stdout().find("...skipped a for lack of foo.h...") == -1)
t.rm('b')
# Now test that if target 'c' also depends on 'b', then it will not be built, as
# well.
t.run_build_system(["-f-"], status=1, stdin=code + " copy c : b ; DEPENDS c : b ; DEPENDS all : c ; ")
t.fail_test(t.stdout().find("...skipped c for lack of foo.h...") == -1)
t.rm('b')
# Now add a rule for creating foo.h.
code += """
actions create-foo
{
echo // > $(<)
}
create-foo foo.h ;
"""
t.run_build_system(["-f-"], stdin=code)
# Run two times, adding explicit dependency from all to foo.h at the beginning
# and at the end, to make sure that foo.h is generated before 'a' in all cases.
def mk_correct_order_func(s1, s2):
def correct_order(s):
n1 = s.find(s1)
n2 = s.find(s2)
return ( n1 != -1 ) and ( n2 != -1 ) and ( n1 < n2 )
return correct_order
correct_order = mk_correct_order_func("create-foo", "copy a")
t.rm(["a", "b", "foo.h"])
t.run_build_system(["-d+2", "-f-"], stdin=code + " DEPENDS all : foo.h ;")
t.fail_test(not correct_order(t.stdout()))
t.rm(["a", "b", "foo.h"])
t.run_build_system(["-d+2", "-f-"], stdin=" DEPENDS all : foo.h ; " + code)
t.fail_test(not correct_order(t.stdout()))
# Now foo.h exists. Test include from b -> foo.h -> bar.h -> biz.h. b and foo.h
# already have updating actions.
t.rm(["a", "b"])
t.write("foo.h", "#include <bar.h>")
t.write("bar.h", "#include <biz.h>")
t.run_build_system(["-d+2", "-f-"], status=1, stdin=code)
t.fail_test(t.stdout().find("...skipped a for lack of biz.h...") == -1)
# Add an action for biz.h.
code += """
actions create-biz
{
echo // > $(<)
}
create-biz biz.h ;
"""
t.rm(["b"])
correct_order = mk_correct_order_func("create-biz", "copy a")
t.run_build_system(["-d+2", "-f-"], stdin=code + " DEPENDS all : biz.h ;")
t.fail_test(not correct_order(t.stdout()))
t.rm(["a", "biz.h"])
t.run_build_system(["-d+2", "-f-"], stdin=" DEPENDS all : biz.h ; " + code)
t.fail_test(not correct_order(t.stdout()))
t.write("a", "")
code="""
DEPENDS all : main d ;
actions copy
{
cp $(>) $(<) || copy $(>) $(<)
}
DEPENDS main : a ;
copy main : a ;
INCLUDES a : <1>c ;
NOCARE <1>c ;
SEARCH on <1>c = . ;
actions create-c
{
echo d > $(<)
}
actions create-d
{
echo // > $(<)
}
create-c <2>c ;
LOCATE on <2>c = . ;
create-d d ;
HDRSCAN on <1>c = (.*) ;
HDRRULE on <1>c = hdrrule ;
rule hdrrule
{
INCLUDES $(1) : d ;
}
"""
correct_order = mk_correct_order_func("create-d", "copy main")
t.run_build_system(["-d2", "-f-"], stdin=code)
t.fail_test(not correct_order(t.stdout()))
t.cleanup()

View File

@@ -0,0 +1,139 @@
#!/usr/bin/python
# Copyright 2017 Steven Watanabe
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
import BoostBuild
def test_basic():
t = BoostBuild.Tester(pass_toolset=0)
t.write("file.jam", """\
actions fail
{
invalid-dd0eeb5899734622
}
FAIL_EXPECTED t1 ;
fail t1 ;
UPDATE t1 ;
""")
t.run_build_system(["-ffile.jam"])
t.expect_output_lines("...failed*", False)
t.expect_nothing_more()
t.cleanup()
def test_error():
t = BoostBuild.Tester(pass_toolset=0)
t.write("file.jam", """\
actions pass
{
echo okay >$(<)
}
FAIL_EXPECTED t1 ;
pass t1 ;
UPDATE t1 ;
""")
t.run_build_system(["-ffile.jam"], status=1)
t.expect_output_lines("...failed pass t1...")
t.expect_nothing_more()
t.cleanup()
def test_multiple_actions():
"""FAIL_EXPECTED targets are considered to pass if the first
updating action fails. Further actions will be skipped."""
t = BoostBuild.Tester(pass_toolset=0)
t.write("file.jam", """\
actions fail
{
invalid-dd0eeb5899734622
}
actions pass
{
echo okay >$(<)
}
FAIL_EXPECTED t1 ;
fail t1 ;
pass t1 ;
UPDATE t1 ;
""")
t.run_build_system(["-ffile.jam", "-d1"])
t.expect_output_lines("...failed*", False)
t.expect_output_lines("pass t1", False)
t.expect_nothing_more()
t.cleanup()
def test_quitquick():
"""Tests that FAIL_EXPECTED targets do not cause early exit
on failure."""
t = BoostBuild.Tester(pass_toolset=0)
t.write("file.jam", """\
actions fail
{
invalid-dd0eeb5899734622
}
actions pass
{
echo okay >$(<)
}
FAIL_EXPECTED t1 ;
fail t1 ;
pass t2 ;
UPDATE t1 t2 ;
""")
t.run_build_system(["-ffile.jam", "-q", "-d1"])
t.expect_output_lines("pass t2")
t.expect_addition("t2")
t.expect_nothing_more()
t.cleanup()
def test_quitquick_error():
"""FAIL_EXPECTED targets should cause early exit if they unexpectedly pass."""
t = BoostBuild.Tester(pass_toolset=0)
t.write("file.jam", """\
actions pass
{
echo okay >$(<)
}
FAIL_EXPECTED t1 ;
pass t1 ;
pass t2 ;
UPDATE t1 t2 ;
""")
t.run_build_system(["-ffile.jam", "-q", "-d1"], status=1)
t.expect_output_lines("pass t2", False)
t.expect_nothing_more()
t.cleanup()
test_basic()
test_error()
test_multiple_actions()
test_quitquick()
test_quitquick_error()

View File

@@ -0,0 +1,82 @@
#!/usr/bin/python
# Copyright 2003 Vladimir Prus
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
import BoostBuild
t = BoostBuild.Tester(pass_toolset=0)
t.write("code", """\
module a
{
rule r1 ( )
{
ECHO R1 ;
}
local rule l1 ( )
{
ECHO A.L1 ;
}
}
module a2
{
rule r2 ( )
{
ECHO R2 ;
}
}
IMPORT a2 : r2 : : a2.r2 ;
rule a.l1 ( )
{
ECHO L1 ;
}
module b
{
IMPORT_MODULE a : b ;
rule test
{
# Call rule visible via IMPORT_MODULE
a.r1 ;
# Call rule in global scope
a2.r2 ;
# Call rule in global scope. Doesn't find local rule
a.l1 ;
# Make l1 visible
EXPORT a : l1 ;
a.l1 ;
}
}
IMPORT b : test : : test ;
test ;
module c
{
rule test
{
ECHO CTEST ;
}
}
IMPORT_MODULE c : ;
c.test ;
EXIT : 0 ;
""")
t.run_build_system(["-fcode"], stdout="""\
R1
R2
L1
A.L1
CTEST
""")
t.cleanup()

View File

@@ -0,0 +1,55 @@
#!/usr/bin/python
# Copyright 2014 Steven Watanabe
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
import BoostBuild
import sys
t = BoostBuild.Tester(pass_toolset=False)
t.write("file.jam", """
actions run {
$(ACTION)
}
# Raw commands only work on Windows
if $(OS) = NT
{
JAMSHELL on test-raw = % ;
JAMSHELL on test-raw-fail = % ;
}
ACTION on test-raw = "\"$(PYTHON)\" -V" ;
run test-raw ;
ACTION on test-raw-fail = missing-executable ;
run test-raw-fail ;
# On Windows, the command is stored in a temporary
# file. On other systems it is passed directly.
if $(OS) = NT
{
JAMSHELL on test-py = $(PYTHON) ;
}
else
{
JAMSHELL on test-py = $(PYTHON) -c ;
}
ACTION on test-py = "
from __future__ import print_function
print(\\\",\\\".join([str(x) for x in range(3)]))
" ;
run test-py ;
DEPENDS all : test-raw test-raw-fail test-py ;
""")
t.run_build_system(["-ffile.jam", "-d1", "-sPYTHON=" + sys.executable], status=1)
t.expect_output_lines([
"...failed run test-raw-fail...",
"0,1,2",
"...failed updating 1 target...",
"...updated 2 targets..."])
t.cleanup()

View File

@@ -0,0 +1,12 @@
#!/usr/bin/python
# Copyright 2002, 2003 Vladimir Prus
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
import BoostBuild
t = BoostBuild.Tester(pass_toolset=0)
t.set_tree("core-language")
t.run_build_system(["-ftest.jam"])
t.cleanup()

View File

@@ -0,0 +1,50 @@
#!/usr/bin/python
# Copyright 2003 Vladimir Prus
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
# This tests the "existing" and "updated" modifiers on actions.
import BoostBuild
t = BoostBuild.Tester(pass_toolset=0)
code = """
DEPENDS all : a ;
ALWAYS a ;
NOTFILE a ;
actions existing make-a
{
echo $(>) > list
}
make-a a : a-1 a-2 a-3 ;
DEPENDS a : a-1 a-2 a-3 ;
NOCARE a-1 a-2 ;
actions make-a3
{
echo foo > $(<)
}
make-a3 a-3 ;
"""
t.write("file.jam", code)
t.write("a-1", "")
t.run_build_system(["-ffile.jam"])
t.fail_test(t.read("list").strip() != "a-1")
t.rm(["a-3", "list"])
code = code.replace("existing", "updated")
t.write("file.jam", code)
t.run_build_system(["-ffile.jam"])
t.fail_test(t.read("list").strip() != "a-3")
code = code.replace("updated", "existing updated")
t.write("file.jam", code)
t.run_build_system(["-ffile.jam"])
t.fail_test(t.read("list").strip() != "a-1 a-3")
t.cleanup()

View File

@@ -0,0 +1,202 @@
#!/usr/bin/python
# Copyright 2013 Steven Watanabe
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
# Tests that actions that produce multiple targets are handled
# correctly. The rules are as follows:
#
# - If any action that updates a target is run, then the target
# is considered to be out-of-date and all of its updating actions
# are run in order.
# - A target is considered updated when all of its updating actions
# have completed successfully.
# - If any updating action for a target fails, then the remaining
# actions are skipped and the target is marked as failed.
#
# Note that this is a more thorough test case for the same
# problem that core_parallel_multifile_actions_N.py checks for.
import BoostBuild
t = BoostBuild.Tester(["-d1"], pass_toolset=0)
t.write("file.jam", """
actions update
{
echo updating $(<)
}
update x1 x2 ;
update x2 x3 ;
""")
# Updating x1 should force x2 to update as well.
t.run_build_system(["-ffile.jam", "x1"], stdout="""\
...found 3 targets...
...updating 3 targets...
update x1
updating x1 x2
update x2
updating x2 x3
...updated 3 targets...
""")
# If x1 is up-to-date, we don't need to update x2,
# even though x2 is missing.
t.write("x1", "")
t.run_build_system(["-ffile.jam", "x1"], stdout="""\
...found 1 target...
""")
# Building x3 should update x1 and x2, even though
# x1 would be considered up-to-date, taken alone.
t.run_build_system(["-ffile.jam", "x3"], stdout="""\
...found 3 targets...
...updating 2 targets...
update x1
updating x1 x2
update x2
updating x2 x3
...updated 3 targets...
""")
# Updating x2 should succeed, but x3 should be skipped
t.rm("x1")
t.write("file.jam", """\
actions update
{
echo updating $(<)
}
actions fail
{
echo failed $(<)
exit 1
}
update x1 x2 ;
fail x1 ;
update x1 x3 ;
update x2 ;
update x3 ;
""")
t.run_build_system(["-ffile.jam", "x3"], status=1, stdout="""\
...found 3 targets...
...updating 3 targets...
update x1
updating x1 x2
fail x1
failed x1
echo failed x1
exit 1
...failed fail x1...
update x2
updating x2
...failed updating 2 targets...
...updated 1 target...
""")
# Make sure that dependencies of targets that are
# updated as a result of a multifile action are
# processed correctly.
t.rm("x1")
t.write("file.jam", """\
actions update
{
echo updating $(<)
}
update x1 ;
update x2 ;
DEPENDS x2 : x1 ;
update x2 x3 ;
""")
t.run_build_system(["-ffile.jam", "x3"], stdout="""\
...found 3 targets...
...updating 3 targets...
update x1
updating x1
update x2
updating x2
update x2
updating x2 x3
...updated 3 targets...
""")
# JAM_SEMAPHORE rules:
#
# - if two updating actions have targets that share a semaphore,
# these actions cannot be run in parallel.
#
t.write("file.jam", """\
actions update
{
echo updating $(<)
}
targets = x1 x2 ;
JAM_SEMAPHORE on $(targets) = <s>update_sem ;
update x1 x2 ;
""")
t.run_build_system(["-ffile.jam", "x1"], stdout="""\
...found 2 targets...
...updating 2 targets...
update x1
updating x1 x2
...updated 2 targets...
""")
# A target can appear multiple times in an action
t.write("file.jam", """\
actions update
{
echo updating $(<)
}
update x1 x1 ;
""")
t.run_build_system(["-ffile.jam", "x1"], stdout="""\
...found 1 target...
...updating 1 target...
update x1
updating x1 x1
...updated 1 target...
""")
# Together actions should check that all the targets are the same
# before combining.
t.write("file.jam", """\
actions together update
{
echo updating $(<) : $(>)
}
update x1 x2 : s1 ;
update x1 x2 : s2 ;
update x3 : s3 ;
update x3 x4 : s4 ;
update x4 x3 : s5 ;
DEPENDS all : x1 x2 x3 x4 ;
""")
t.run_build_system(["-ffile.jam"], stdout="""\
...found 5 targets...
...updating 4 targets...
update x1
updating x1 x2 : s1 s2
update x3
updating x3 : s3
update x3
updating x3 x4 : s4
update x4
updating x4 x3 : s5
...updated 4 targets...
""")
t.cleanup()

View File

@@ -0,0 +1,266 @@
#!/usr/bin/python
# Copyright 2001 Dave Abrahams
# Copyright 2011 Steven Watanabe
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
# Tests Windows command line construction.
#
# Note that the regular 'echo' is an internal shell command on Windows and
# therefore can not be called directly as a standalone Windows process.
import BoostBuild
import os
import re
import sys
executable = sys.executable.replace("\\", "/")
if " " in executable:
executable = '"%s"' % executable
def string_of_length(n):
if n <= 0:
return ""
n -= 1
y = ['', '$(1x10-1)', '$(10x10-1)', '$(100x10-1)', '$(1000x10-1)']
result = []
for i in reversed(range(5)):
x, n = divmod(n, 10 ** i)
result += [y[i]] * x
result.append('x')
return " ".join(result)
# Boost Jam currently does not allow preparing actions with completely empty
# content and always requires at least a single whitespace after the opening
# brace in order to satisfy its Boost Jam language grammar rules.
def test_raw_empty():
whitespace_in = " \n\n\r\r\v\v\t\t \t \r\r \n\n"
# We tell the testing system to read its child process output as raw
# binary data but the bjam process we run will read its input file and
# write out its output as text, i.e. convert all of our "\r\n" sequences to
# "\n" on input and all of its "\n" characters back to "\r\n" on output.
# This means that any lone "\n" input characters not preceded by "\r" will
# get an extra "\r" added in front of it on output.
whitespace_out = whitespace_in.replace("\r\n", "\n").replace("\n", "\r\n")
t = BoostBuild.Tester(["-d2", "-d+4"], pass_toolset=0,
use_test_config=False)
t.write("file.jam", """\
actions do_empty {%s}
JAMSHELL = %% ;
do_empty all ;
""" % (whitespace_in))
t.run_build_system(["-ffile.jam"], universal_newlines=False)
t.expect_output_lines("do_empty all")
t.expect_output_lines("Executing raw command directly", False)
if "\r\n%s\r\n" % whitespace_out not in t.stdout():
BoostBuild.annotation("failure", "Whitespace action content not found "
"on stdout.")
t.fail_test(1, dump_difference=False)
t.cleanup()
def test_raw_nt(n=None, error=False):
t = BoostBuild.Tester(["-d1", "-d+4"], pass_toolset=0,
use_test_config=False)
cmd_prefix = "%s -c \"print('XXX: " % executable
cmd_suffix = "')\""
cmd_extra_length = len(cmd_prefix) + len(cmd_suffix)
if n == None:
n = cmd_extra_length
data_length = n - cmd_extra_length
if data_length < 0:
BoostBuild.annotation("failure", """\
Can not construct Windows command of desired length. Requested command length
too short for the current test configuration.
Requested command length: %d
Minimal supported command length: %d
""" % (n, cmd_extra_length))
t.fail_test(1, dump_difference=False)
# Each $(Xx10-1) variable contains X words of 9 characters each, which,
# including spaces between words, brings the total number of characters in
# its string representation to X * 10 - 1 (X * 9 characters + (X - 1)
# spaces).
t.write("file.jam", """\
ten = 0 1 2 3 4 5 6 7 8 9 ;
1x10-1 = 123456789 ;
10x10-1 = $(ten)12345678 ;
100x10-1 = $(ten)$(ten)1234567 ;
1000x10-1 = $(ten)$(ten)$(ten)123456 ;
actions do_echo
{
%s%s%s
}
JAMSHELL = %% ;
do_echo all ;
""" % (cmd_prefix, string_of_length(data_length), cmd_suffix))
if error:
expected_status = 1
else:
expected_status = 0
t.run_build_system(["-ffile.jam"], status=expected_status)
if error:
t.expect_output_lines("Executing raw command directly", False)
t.expect_output_lines("do_echo action is too long (%d, max 32766):" % n
)
t.expect_output_lines("XXX: *", False)
else:
t.expect_output_lines("Executing raw command directly")
t.expect_output_lines("do_echo action is too long*", False)
m = re.search("^XXX: (.*)$", t.stdout(), re.MULTILINE)
if not m:
BoostBuild.annotation("failure", "Expected output line starting "
"with 'XXX: ' not found.")
t.fail_test(1, dump_difference=False)
if len(m.group(1)) != data_length:
BoostBuild.annotation("failure", """Unexpected output data length.
Expected: %d
Received: %d""" % (n, len(m.group(1))))
t.fail_test(1, dump_difference=False)
t.cleanup()
def test_raw_to_shell_fallback_nt():
t = BoostBuild.Tester(["-d1", "-d+4"], pass_toolset=0,
use_test_config=False)
cmd_prefix = '%s -c print(' % executable
cmd_suffix = ')'
t.write("file_multiline.jam", """\
actions do_multiline
{
echo one
echo two
}
JAMSHELL = % ;
do_multiline all ;
""")
t.run_build_system(["-ffile_multiline.jam"])
t.expect_output_lines("do_multiline all")
t.expect_output_lines("one")
t.expect_output_lines("two")
t.expect_output_lines("Executing raw command directly", False)
t.expect_output_lines("Executing using a command file and the shell: "
"cmd.exe /Q/C")
t.write("file_redirect.jam", """\
actions do_redirect { echo one > two.txt }
JAMSHELL = % ;
do_redirect all ;
""")
t.run_build_system(["-ffile_redirect.jam"])
t.expect_output_lines("do_redirect all")
t.expect_output_lines("one", False)
t.expect_output_lines("Executing raw command directly", False)
t.expect_output_lines("Executing using a command file and the shell: "
"cmd.exe /Q/C")
t.expect_addition("two.txt")
t.write("file_pipe.jam", """\
actions do_pipe
{
echo one | echo two
}
JAMSHELL = % ;
do_pipe all ;
""")
t.run_build_system(["-ffile_pipe.jam"])
t.expect_output_lines("do_pipe all")
t.expect_output_lines("one*", False)
t.expect_output_lines("two")
t.expect_output_lines("Executing raw command directly", False)
t.expect_output_lines("Executing using a command file and the shell: "
"cmd.exe /Q/C")
t.write("file_single_quoted.jam", """\
actions do_single_quoted { %s'5>10'%s }
JAMSHELL = %% ;
do_single_quoted all ;
""" % (cmd_prefix, cmd_suffix))
t.run_build_system(["-ffile_single_quoted.jam"])
t.expect_output_lines("do_single_quoted all")
t.expect_output_lines("5>10")
t.expect_output_lines("Executing raw command directly")
t.expect_output_lines("Executing using a command file and the shell: "
"cmd.exe /Q/C", False)
t.expect_nothing_more()
t.write("file_double_quoted.jam", """\
actions do_double_quoted { %s"5>10"%s }
JAMSHELL = %% ;
do_double_quoted all ;
""" % (cmd_prefix, cmd_suffix))
t.run_build_system(["-ffile_double_quoted.jam"])
t.expect_output_lines("do_double_quoted all")
# The difference between this example and the similar previous one using
# single instead of double quotes stems from how the used Python executable
# parses the command-line string received from Windows.
t.expect_output_lines("False")
t.expect_output_lines("Executing raw command directly")
t.expect_output_lines("Executing using a command file and the shell: "
"cmd.exe /Q/C", False)
t.expect_nothing_more()
t.write("file_escaped_quote.jam", """\
actions do_escaped_quote { %s\\"5>10\\"%s }
JAMSHELL = %% ;
do_escaped_quote all ;
""" % (cmd_prefix, cmd_suffix))
t.run_build_system(["-ffile_escaped_quote.jam"])
t.expect_output_lines("do_escaped_quote all")
t.expect_output_lines("5>10")
t.expect_output_lines("Executing raw command directly", False)
t.expect_output_lines("Executing using a command file and the shell: "
"cmd.exe /Q/C")
t.expect_nothing_more()
t.cleanup()
###############################################################################
#
# main()
# ------
#
###############################################################################
if os.name == 'nt':
test_raw_empty()
# Can not test much shorter lengths as the shortest possible command line
# line length constructed in this depends on the runtime environment, e.g.
# path to the Panther executable running this test.
test_raw_nt()
test_raw_nt(255)
test_raw_nt(1000)
test_raw_nt(8000)
test_raw_nt(8191)
test_raw_nt(8192)
test_raw_nt(10000)
test_raw_nt(30000)
test_raw_nt(32766)
# CreateProcessA() Windows API places a limit of 32768 on the allowed
# command-line length, including a trailing Unicode (2-byte) nul-terminator
# character.
test_raw_nt(32767, error=True)
test_raw_nt(40000, error=True)
test_raw_nt(100001, error=True)
test_raw_to_shell_fallback_nt()

View File

@@ -0,0 +1,55 @@
#!/usr/bin/python
# Copyright 2007 Rene Rivera.
# Copyright 2011 Steven Watanabe
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
import BoostBuild
t = BoostBuild.Tester(pass_toolset=0)
t.write("file.jam", """\
actions .a.
{
echo [$(<:B)] 0
echo [$(<:B)] 1
echo [$(<:B)] 2
}
rule .a.
{
DEPENDS $(<) : $(>) ;
}
NOTFILE subtest ;
.a. subtest_a : subtest ;
.a. subtest_b : subtest ;
DEPENDS all : subtest_a subtest_b ;
""")
t.run_build_system(["-ffile.jam", "-d2"], stdout="""\
...found 4 targets...
...updating 2 targets...
.a. subtest_a
echo [subtest_a] 0
echo [subtest_a] 1
echo [subtest_a] 2
[subtest_a] 0
[subtest_a] 1
[subtest_a] 2
.a. subtest_b
echo [subtest_b] 0
echo [subtest_b] 1
echo [subtest_b] 2
[subtest_b] 0
[subtest_b] 1
[subtest_b] 2
...updated 2 targets...
""")
t.cleanup()

View File

@@ -0,0 +1,44 @@
#!/usr/bin/python
# Copyright 2007 Rene Rivera.
# Copyright 2011 Steven Watanabe
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
import BoostBuild
t = BoostBuild.Tester(pass_toolset=0)
t.write("sleep.bat", """\
::@timeout /T %1 /NOBREAK >nul
@ping 127.0.0.1 -n 2 -w 1000 >nul
@ping 127.0.0.1 -n %1 -w 1000 >nul
@exit /B 0
""")
t.write("file.jam", """\
if $(NT)
{
SLEEP = @call sleep.bat ;
}
else
{
SLEEP = sleep ;
}
actions .a. {
echo 001
$(SLEEP) 4
echo 002
}
.a. sleeper ;
DEPENDS all : sleeper ;
""")
t.run_build_system(["-ffile.jam", "-d1", "-l2"], status=1)
t.expect_output_lines("2 second time limit exceeded")
t.cleanup()

View File

@@ -0,0 +1,51 @@
#!/usr/bin/python
# Copyright 2007 Rene Rivera.
# Copyright 2011 Steven Watanabe
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
import BoostBuild
t = BoostBuild.Tester(pass_toolset=0)
t.write("file.jam", """\
actions .a.
{
echo [$(<:B)] 0
echo [$(<:B)] 1
echo [$(<:B)] 2
}
rule .a.
{
DEPENDS $(<) : $(>) ;
}
NOTFILE subtest ;
.a. subtest_a : subtest ;
.a. subtest_b : subtest ;
FAIL_EXPECTED subtest_b ;
DEPENDS all : subtest_a subtest_b ;
""")
t.run_build_system(["-ffile.jam", "-n"], stdout="""\
...found 4 targets...
...updating 2 targets...
.a. subtest_a
echo [subtest_a] 0
echo [subtest_a] 1
echo [subtest_a] 2
.a. subtest_b
echo [subtest_b] 0
echo [subtest_b] 1
echo [subtest_b] 2
...updated 2 targets...
""")
t.expect_nothing_more()
t.cleanup()

View File

@@ -0,0 +1,103 @@
#!/usr/bin/python
# Copyright 2006 Rene Rivera.
# Copyright 2011 Steven Watanabe
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
import BoostBuild
t = BoostBuild.Tester(["-d1"], pass_toolset=0)
t.write("sleep.bat", """\
::@timeout /T %1 /NOBREAK >nul
@ping 127.0.0.1 -n 2 -w 1000 >nul
@ping 127.0.0.1 -n %1 -w 1000 >nul
@exit /B 0
""")
t.write("file.jam", """\
if $(NT)
{
actions sleeper
{
echo [$(<:S)] 0
call sleep.bat 1
echo [$(<:S)] 1
call sleep.bat 1
echo [$(<:S)] 2
call sleep.bat $(<:B)
}
}
else
{
actions sleeper
{
echo "[$(<:S)] 0"
sleep 1
echo "[$(<:S)] 1"
sleep 1
echo "[$(<:S)] 2"
sleep $(<:B)
}
}
rule sleeper
{
DEPENDS $(<) : $(>) ;
}
NOTFILE front ;
sleeper 1.a : front ;
sleeper 2.a : front ;
sleeper 3.a : front ;
sleeper 4.a : front ;
NOTFILE choke ;
DEPENDS choke : 1.a 2.a 3.a 4.a ;
sleeper 1.b : choke ;
sleeper 2.b : choke ;
sleeper 3.b : choke ;
sleeper 4.b : choke ;
DEPENDS bottom : 1.b 2.b 3.b 4.b ;
DEPENDS all : bottom ;
""")
t.run_build_system(["-ffile.jam", "-j4"], stdout="""\
...found 12 targets...
...updating 8 targets...
sleeper 1.a
[.a] 0
[.a] 1
[.a] 2
sleeper 2.a
[.a] 0
[.a] 1
[.a] 2
sleeper 3.a
[.a] 0
[.a] 1
[.a] 2
sleeper 4.a
[.a] 0
[.a] 1
[.a] 2
sleeper 1.b
[.b] 0
[.b] 1
[.b] 2
sleeper 2.b
[.b] 0
[.b] 1
[.b] 2
sleeper 3.b
[.b] 0
[.b] 1
[.b] 2
sleeper 4.b
[.b] 0
[.b] 1
[.b] 2
...updated 8 targets...
""")
t.cleanup()

View File

@@ -0,0 +1,78 @@
#!/usr/bin/python
# Copyright 2007 Rene Rivera.
# Copyright 2011 Steven Watanabe
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
# Added to guard against a bug causing targets to be used before they
# themselves have finished building. This used to happen for targets built by a
# multi-file action that got triggered by another target.
#
# Example:
# When target A and target B were declared as created by a single action and
# target A triggered running that action then, while the action was still
# running, target B was already reporting as being built causing other targets
# depending on target A to be built prematurely.
import BoostBuild
t = BoostBuild.Tester(["-d1"], pass_toolset=0)
t.write("sleep.bat", """\
::@timeout /T %1 /NOBREAK >nul
@ping 127.0.0.1 -n 2 -w 1000 >nul
@ping 127.0.0.1 -n %1 -w 1000 >nul
@exit /B 0
""")
t.write("file.jam", """\
if $(NT)
{
SLEEP = @call sleep.bat ;
}
else
{
SLEEP = sleep ;
}
actions .gen.
{
echo 001
$(SLEEP) 4
echo 002
}
rule .use.1 { DEPENDS $(<) : $(>) ; }
actions .use.1
{
echo 003
}
rule .use.2 { DEPENDS $(<) : $(>) ; }
actions .use.2
{
$(SLEEP) 1
echo 004
}
.gen. g1.generated g2.generated ;
.use.1 u1.user : g1.generated ;
.use.2 u2.user : g2.generated ;
DEPENDS all : u1.user u2.user ;
""")
t.run_build_system(["-ffile.jam", "-j2"], stdout="""\
...found 5 targets...
...updating 4 targets...
.gen. g1.generated
001
002
.use.1 u1.user
003
.use.2 u2.user
004
...updated 4 targets...
""")
t.cleanup()

View File

@@ -0,0 +1,71 @@
#!/usr/bin/python
# Copyright 2008 Jurko Gospodnetic, Vladimir Prus
# Copyright 2011 Steven Watanabe
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
# Added to guard against a bug causing targets to be used before they
# themselves have finished building. This used to happen for targets built by a
# multi-file action that got triggered by another target, except when the
# target triggering the action was the first one in the list of targets
# produced by that action.
#
# Example:
# When target A and target B were declared as created by a single action with
# A being the first one listed, and target B triggered running that action
# then, while the action was still running, target A was already reporting as
# being built causing other targets depending on target A to be built
# prematurely.
import BoostBuild
t = BoostBuild.Tester(pass_toolset=0)
t.write("sleep.bat", """\
::@timeout /T %1 /NOBREAK >nul
@ping 127.0.0.1 -n 2 -w 1000 >nul
@ping 127.0.0.1 -n %1 -w 1000 >nul
@exit /B 0
""")
t.write("file.jam", """\
if $(NT)
{
SLEEP = @call sleep.bat ;
}
else
{
SLEEP = sleep ;
}
actions link
{
$(SLEEP) 1
echo 001 - linked
}
link dll lib ;
actions install
{
echo 002 - installed
}
install installed_dll : dll ;
DEPENDS installed_dll : dll ;
DEPENDS all : lib installed_dll ;
""")
t.run_build_system(["-ffile.jam", "-j2"], stdout="""\
...found 4 targets...
...updating 3 targets...
link dll
001 - linked
install installed_dll
002 - installed
...updated 3 targets...
""")
t.cleanup()

View File

@@ -0,0 +1,36 @@
#!/usr/bin/python
# Copyright 2018 Steven Watanabe
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
# Tests the parsing of tokens
import BoostBuild
t = BoostBuild.Tester(pass_toolset=0)
t.write("file.jam", """\
rule test1 ( args * )
{
EXIT $(args) : 0 ;
}
test1
a # a comment
# another comment
b
c #| a multiline comment |# d
#| another
multiline
comment
|#
e "#f" ;
""")
t.run_build_system(["-ffile.jam"], stdout="""\
a b c d e #f
""")
t.cleanup()

View File

@@ -0,0 +1,74 @@
#!/usr/bin/python
# Copyright 2012. Jurko Gospodnetic
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
# Test Boost Jam parser's source line tracking & reporting.
import BoostBuild
def test_eof_in_string():
t = BoostBuild.Tester(pass_toolset=False)
t.write("file.jam", '\n\n\naaa = "\n\n\n\n\n\n')
t.run_build_system(["-ffile.jam"], status=1)
t.expect_output_lines('file.jam:4: unmatched " in string at keyword =')
t.expect_output_lines("file.jam:4: syntax error at EOF")
t.cleanup()
def test_error_missing_argument(eof):
"""
This use case used to cause a missing argument error to be reported in
module '(builtin)' in line -1 when the input file did not contain a
trailing newline.
"""
t = BoostBuild.Tester(pass_toolset=False)
t.write("file.jam", """\
rule f ( param ) { }
f ;%s""" % __trailing_newline(eof))
t.run_build_system(["-ffile.jam"], status=1)
t.expect_output_lines("file.jam:2: in module scope")
t.expect_output_lines("file.jam:1:see definition of rule 'f' being called")
t.cleanup()
def test_error_syntax(eof):
t = BoostBuild.Tester(pass_toolset=False)
t.write("file.jam", "ECHO%s" % __trailing_newline(eof))
t.run_build_system(["-ffile.jam"], status=1)
t.expect_output_lines("file.jam:1: syntax error at EOF")
t.cleanup()
def test_traceback():
t = BoostBuild.Tester(pass_toolset=False)
t.write("file.jam", """\
NOTFILE all ;
ECHO [ BACKTRACE ] ;""")
t.run_build_system(["-ffile.jam"])
t.expect_output_lines("file.jam 2 module scope")
t.cleanup()
def __trailing_newline(eof):
"""
Helper function returning an empty string or a newling character to
append to the current output line depending on whether we want that line to
be the last line in the file (eof == True) or not (eof == False).
"""
if eof:
return ""
return "\n"
test_error_missing_argument(eof=False)
test_error_missing_argument(eof=True)
test_error_syntax(eof=False)
test_error_syntax(eof=True)
test_traceback()
test_eof_in_string()

View File

@@ -0,0 +1,23 @@
#!/usr/bin/python
# Copyright (C) Mateusz Loskot 2020.
# Distributed under the Boost Software License, Version 1.0. (See
# accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
# Test that Jam syntax error results in non-zero exit status
import BoostBuild
# Create a temporary working directory.
t = BoostBuild.Tester()
# Create the needed files.
t.write("jamroot.jam", """
exe hello : hello.cpp
""")
t.run_build_system(status=1)
t.cleanup()

View File

@@ -0,0 +1,47 @@
#!/usr/bin/python
# Copyright 2003 Vladimir Prus
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
# This tests the typechecking facilities.
import BoostBuild
t = BoostBuild.Tester(["-ffile.jam"], pass_toolset=0)
t.write("file.jam", """
module .typecheck
{
rule "[path]" ( x )
{
if ! [ MATCH "^(::)" : $(x) ]
{
ECHO "Error: $(x) is not a path" ;
return true ;
}
}
}
rule do ( [path] a )
{
}
do $(ARGUMENT) ;
actions dummy { }
dummy all ;
""")
t.run_build_system(["-sARGUMENT=::a/b/c"])
t.run_build_system(["-sARGUMENT=a/b/c"], status=1, stdout="""\
Error: a/b/c is not a path
file.jam:18: in module scope
*** argument error
* rule do ( [path] a )
* called with: ( a/b/c )
* true a
file.jam:16:see definition of rule 'do' being called
""")
t.cleanup()

View File

@@ -0,0 +1,377 @@
#!/usr/bin/python
# Copyright 2011 Steven Watanabe
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
import BoostBuild
import os
def basic():
t = BoostBuild.Tester(pass_toolset=0)
t.write("file.jam", """\
actions do-print
{
echo updating $(<)
}
NOTFILE target1 ;
ALWAYS target1 ;
do-print target1 ;
UPDATE_NOW target1 ;
DEPENDS all : target1 ;
""")
t.run_build_system(["-ffile.jam"], stdout="""\
...found 1 target...
...updating 1 target...
do-print target1
updating target1
...updated 1 target...
...found 1 target...
""")
t.cleanup()
def ignore_minus_n():
t = BoostBuild.Tester(pass_toolset=0)
t.write("file.jam", """\
actions do-print
{
echo updating $(<)
}
NOTFILE target1 ;
ALWAYS target1 ;
do-print target1 ;
UPDATE_NOW target1 : : ignore-minus-n ;
DEPENDS all : target1 ;
""")
t.run_build_system(["-ffile.jam", "-n"], stdout="""\
...found 1 target...
...updating 1 target...
do-print target1
echo updating target1
updating target1
...updated 1 target...
...found 1 target...
""")
t.cleanup()
def failed_target():
t = BoostBuild.Tester(pass_toolset=0)
t.write("file.jam", """\
actions fail
{
exit 1
}
NOTFILE target1 ;
ALWAYS target1 ;
fail target1 ;
actions do-print
{
echo updating $(<)
}
NOTFILE target2 ;
do-print target2 ;
DEPENDS target2 : target1 ;
UPDATE_NOW target1 : : ignore-minus-n ;
DEPENDS all : target1 target2 ;
""")
t.run_build_system(["-ffile.jam", "-n"], stdout="""\
...found 1 target...
...updating 1 target...
fail target1
exit 1
...failed fail target1...
...failed updating 1 target...
...found 2 targets...
...updating 1 target...
do-print target2
echo updating target2
...updated 1 target...
""")
t.cleanup()
def missing_target():
t = BoostBuild.Tester(pass_toolset=0)
t.write("file.jam", """\
actions do-print
{
echo updating $(<)
}
NOTFILE target2 ;
do-print target2 ;
DEPENDS target2 : target1 ;
UPDATE_NOW target1 : : ignore-minus-n ;
DEPENDS all : target1 target2 ;
""")
t.run_build_system(["-ffile.jam", "-n"], status=1, stdout="""\
don't know how to make target1
...found 1 target...
...can't find 1 target...
...found 2 targets...
...can't make 1 target...
""")
t.cleanup()
def build_once():
"""
Make sure that if we call UPDATE_NOW with ignore-minus-n, the target gets
updated exactly once regardless of previous calls to UPDATE_NOW with -n in
effect.
"""
t = BoostBuild.Tester(pass_toolset=0)
t.write("file.jam", """\
actions do-print
{
echo updating $(<)
}
NOTFILE target1 ;
ALWAYS target1 ;
do-print target1 ;
UPDATE_NOW target1 ;
UPDATE_NOW target1 : : ignore-minus-n ;
UPDATE_NOW target1 : : ignore-minus-n ;
DEPENDS all : target1 ;
""")
t.run_build_system(["-ffile.jam", "-n"], stdout="""\
...found 1 target...
...updating 1 target...
do-print target1
echo updating target1
...updated 1 target...
do-print target1
echo updating target1
updating target1
...updated 1 target...
...found 1 target...
""")
t.cleanup()
def return_status():
"""
Make sure that UPDATE_NOW returns a failure status if
the target failed in a previous call to UPDATE_NOW
"""
t = BoostBuild.Tester(pass_toolset=0)
t.write("file.jam", """\
actions fail
{
exit 1
}
NOTFILE target1 ;
ALWAYS target1 ;
fail target1 ;
ECHO "update1:" [ UPDATE_NOW target1 ] ;
ECHO "update2:" [ UPDATE_NOW target1 ] ;
DEPENDS all : target1 ;
""")
t.run_build_system(["-ffile.jam"], status=1, stdout="""\
...found 1 target...
...updating 1 target...
fail target1
exit 1
...failed fail target1...
...failed updating 1 target...
update1:
update2:
...found 1 target...
""")
t.cleanup()
def save_restore():
"""Tests that ignore-minus-n and ignore-minus-q are
local to the call to UPDATE_NOW"""
t = BoostBuild.Tester(pass_toolset=0)
t.write("actions.jam", """\
rule fail
{
NOTFILE $(<) ;
ALWAYS $(<) ;
}
actions fail
{
exit 1
}
rule pass
{
NOTFILE $(<) ;
ALWAYS $(<) ;
}
actions pass
{
echo updating $(<)
}
""")
t.write("file.jam", """
include actions.jam ;
fail target1 ;
fail target2 ;
UPDATE_NOW target1 target2 : : $(IGNORE_MINUS_N) : $(IGNORE_MINUS_Q) ;
fail target3 ;
fail target4 ;
UPDATE_NOW target3 target4 ;
UPDATE ;
""")
t.run_build_system(['-n', '-sIGNORE_MINUS_N=1', '-ffile.jam'],
stdout='''...found 2 targets...
...updating 2 targets...
fail target1
exit 1
...failed fail target1...
fail target2
exit 1
...failed fail target2...
...failed updating 2 targets...
...found 2 targets...
...updating 2 targets...
fail target3
exit 1
fail target4
exit 1
...updated 2 targets...
''')
t.run_build_system(['-q', '-sIGNORE_MINUS_N=1', '-ffile.jam'],
status=1, stdout='''...found 2 targets...
...updating 2 targets...
fail target1
exit 1
...failed fail target1...
...failed updating 1 target...
...found 2 targets...
...updating 2 targets...
fail target3
exit 1
...failed fail target3...
...failed updating 1 target...
''')
t.run_build_system(['-n', '-sIGNORE_MINUS_Q=1', '-ffile.jam'],
stdout='''...found 2 targets...
...updating 2 targets...
fail target1
exit 1
fail target2
exit 1
...updated 2 targets...
...found 2 targets...
...updating 2 targets...
fail target3
exit 1
fail target4
exit 1
...updated 2 targets...
''')
t.run_build_system(['-q', '-sIGNORE_MINUS_Q=1', '-ffile.jam'],
status=1, stdout='''...found 2 targets...
...updating 2 targets...
fail target1
exit 1
...failed fail target1...
fail target2
exit 1
...failed fail target2...
...failed updating 2 targets...
...found 2 targets...
...updating 2 targets...
fail target3
exit 1
...failed fail target3...
...failed updating 1 target...
''')
t.cleanup()
basic()
ignore_minus_n()
failed_target()
missing_target()
build_once()
return_status()
save_restore()

View File

@@ -0,0 +1,39 @@
#!/usr/bin/python
# Copyright 2012. Jurko Gospodnetic
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
# Tests that variables in actions get expanded but double quote characters
# get treated as regular characters and not string literal delimiters when
# determining string tokens concatenated to the variable being expanded.
#
# We also take care to make this test work correctly when run using both
# Windows and Unix echo command variant. That is why we add the extra single
# quotes around the text being echoed - they will make the double quotes be
# displayed as regular characters in both cases but will be displayed
# themselves only when using the Windows cmd shell's echo command.
import BoostBuild
t = BoostBuild.Tester(pass_toolset=0)
t.write("file.jam", """\
rule dummy ( i )
{
local a = 1 2 3 ;
ECHO From "rule:" $(a)" seconds" ;
a on $(i) = $(a) ;
}
actions dummy
{
echo 'From action: $(a)" seconds"'
}
dummy all ;
""")
t.run_build_system(["-ffile.jam", "-d1"])
t.expect_output_lines("From rule: 1 seconds 2 seconds 3 seconds")
t.expect_output_lines('*From action: 1" 2" 3" seconds"*')
t.cleanup()

View File

@@ -0,0 +1,38 @@
#!/usr/bin/python
# Copyright 2003 Dave Abrahams
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
# This tests the core rule for enumerating the variable names in a module.
import BoostBuild
t = BoostBuild.Tester(pass_toolset=0)
t.write("file.jam", """\
module foo
{
rule bar { }
var1 = x y ;
var2 = fubar ;
}
expected = var1 var2 ;
names = [ VARNAMES foo ] ;
if $(names) in $(expected) && $(expected) in $(names)
{
# everything OK
}
else
{
EXIT expected to find variables $(expected:J=", ") in module foo,
but found $(names:J=", ") instead. ;
}
DEPENDS all : xx ;
NOTFILE xx ;
""")
t.run_build_system(["-ffile.jam"], status=0)
t.cleanup()

View File

@@ -0,0 +1,66 @@
#!/usr/bin/python
# Copyright 2003, 2004, 2005 Vladimir Prus
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
# Attempt to declare a generator for creating OBJ from RC files. That generator
# should be considered together with standard CPP->OBJ generators and
# successfully create the target. Since we do not have a RC compiler everywhere,
# we fake the action. The resulting OBJ will be unusable, but it must be
# created.
import BoostBuild
t = BoostBuild.Tester()
t.write("jamroot.jam", """
import rcc ;
""")
t.write("rcc.jam", """
import type ;
import generators ;
import print ;
# Use 'RCC' to avoid conflicts with definitions in the standard rc.jam and
# msvc.jam
type.register RCC : rcc ;
rule resource-compile ( targets * : sources * : properties * )
{
print.output $(targets[1]) ;
print.text "rc-object" ;
}
generators.register-standard rcc.resource-compile : RCC : OBJ ;
""")
t.write("rcc.py", """
import b2.build.type as type
import b2.build.generators as generators
from b2.manager import get_manager
# Use 'RCC' to avoid conflicts with definitions in the standard rc.jam and
# msvc.jam
type.register('RCC', ['rcc'])
generators.register_standard("rcc.resource-compile", ["RCC"], ["OBJ"])
get_manager().engine().register_action(
"rcc.resource-compile",
'@($(STDOUT):E=rc-object) > "$(<)"')
""")
t.write("jamfile.jam", """
obj r : r.rcc ;
""")
t.write("r.rcc", """
""")
t.run_build_system()
t.expect_content("bin/r.obj", "rc-object")
t.cleanup()

View File

@@ -0,0 +1,326 @@
#!/usr/bin/python
# Copyright 2016 Steven Watanabe
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
# Test the mi interface for the debugger
import BoostBuild
import TestCmd
import re
def split_stdin_stdout(text):
"""stdin is all text after the prompt up to and including
the next newline. Everything else is stdout. stdout
may contain regular expressions enclosed in {{}}."""
prompt = re.escape('(gdb) \n')
pattern = re.compile('(?<=%s)((?:\d*-.*)\n)' % prompt)
stdin = ''.join(re.findall(pattern, text))
stdout = re.sub(pattern, '', text)
outside_pattern = re.compile(r'(?:\A|(?<=\}\}))(?:[^\{]|(?:\{(?!\{)))*(?:(?=\{\{)|\Z)')
def escape_line(line):
line = re.sub(outside_pattern, lambda m: re.escape(m.group(0)), line)
return re.sub(r'\{\{|\}\}', '', line)
stdout = '\n'.join([escape_line(line) for line in stdout.split('\n')])
return (stdin,stdout)
def run(tester, io):
(input,output) = split_stdin_stdout(io)
tester.run_build_system(stdin=input, stdout=output, match=TestCmd.match_re)
def make_tester():
return BoostBuild.Tester(["-dmi"], pass_toolset=False, pass_d0=False,
use_test_config=False, ignore_toolset_requirements=False, match=TestCmd.match_re)
def test_exec_run():
t = make_tester()
t.write("test.jam", """\
UPDATE ;
""")
run(t, """\
=thread-group-added,id="i1"
(gdb)
72-exec-run -ftest.jam
=thread-created,id="1",group-id="i1"
72^running
(gdb)
*stopped,reason="exited-normally"
(gdb)
73-gdb-exit
73^exit
""")
t.cleanup()
def test_exit_status():
t = make_tester()
t.write("test.jam", """\
EXIT : 1 ;
""")
run(t, """\
=thread-group-added,id="i1"
(gdb)
72-exec-run -ftest.jam
=thread-created,id="1",group-id="i1"
72^running
(gdb)
*stopped,reason="exited",exit-code="1"
(gdb)
73-gdb-exit
73^exit
""")
t.cleanup()
def test_exec_step():
t = make_tester()
t.write("test.jam", """\
rule g ( )
{
a = 1 ;
b = 2 ;
}
rule f ( )
{
g ;
c = 3 ;
}
f ;
""")
run(t, """\
=thread-group-added,id="i1"
(gdb)
-break-insert f
^done,bkpt={number="1",type="breakpoint",disp="keep",enabled="y",func="f"}
(gdb)
72-exec-run -ftest.jam
=thread-created,id="1",group-id="i1"
72^running
(gdb)
*stopped,reason="breakpoint-hit",bkptno="1",disp="keep",frame={func="f",args=[],file="test.jam",fullname="{{.*}}test.jam",line="8"},thread-id="1",stopped-threads="all"
(gdb)
1-exec-step
1^running
(gdb)
*stopped,reason="end-stepping-range",frame={func="g",args=[],file="test.jam",fullname="{{.*}}test.jam",line="3"},thread-id="1"
(gdb)
2-exec-step
2^running
(gdb)
*stopped,reason="end-stepping-range",frame={func="g",args=[],file="test.jam",fullname="{{.*}}test.jam",line="4"},thread-id="1"
(gdb)
3-exec-step
3^running
(gdb)
*stopped,reason="end-stepping-range",frame={func="f",args=[],file="test.jam",fullname="{{.*}}test.jam",line="9"},thread-id="1"
(gdb)
73-gdb-exit
73^exit
""")
t.cleanup()
def test_exec_next():
t = make_tester()
t.write("test.jam", """\
rule g ( )
{
a = 1 ;
}
rule f ( )
{
g ;
b = 2 ;
c = 3 ;
}
rule h ( )
{
f ;
g ;
}
h ;
d = 4 ;
""")
run(t, """\
=thread-group-added,id="i1"
(gdb)
-break-insert f
^done,bkpt={number="1",type="breakpoint",disp="keep",enabled="y",func="f"}
(gdb)
72-exec-run -ftest.jam
=thread-created,id="1",group-id="i1"
72^running
(gdb)
*stopped,reason="breakpoint-hit",bkptno="1",disp="keep",frame={func="f",args=[],file="test.jam",fullname="{{.*}}test.jam",line="7"},thread-id="1",stopped-threads="all"
(gdb)
1-exec-next
1^running
(gdb)
*stopped,reason="end-stepping-range",frame={func="f",args=[],file="test.jam",fullname="{{.*}}test.jam",line="8"},thread-id="1"
(gdb)
2-exec-next
2^running
(gdb)
*stopped,reason="end-stepping-range",frame={func="f",args=[],file="test.jam",fullname="{{.*}}test.jam",line="9"},thread-id="1"
(gdb)
3-exec-next
3^running
(gdb)
*stopped,reason="end-stepping-range",frame={func="h",args=[],file="test.jam",fullname="{{.*}}test.jam",line="14"},thread-id="1"
(gdb)
4-exec-next
4^running
(gdb)
*stopped,reason="end-stepping-range",frame={func="module scope",args=[],file="test.jam",fullname="{{.*}}test.jam",line="17"},thread-id="1"
(gdb)
73-gdb-exit
73^exit
""")
t.cleanup()
def test_exec_finish():
t = make_tester()
t.write("test.jam", """\
rule f ( )
{
a = 1 ;
}
rule g ( )
{
f ;
b = 2 ;
i ;
}
rule h ( )
{
g ;
i ;
}
rule i ( )
{
c = 3 ;
}
h ;
d = 4 ;
""")
run(t, """\
=thread-group-added,id="i1"
(gdb)
-break-insert f
^done,bkpt={number="1",type="breakpoint",disp="keep",enabled="y",func="f"}
(gdb)
72-exec-run -ftest.jam
=thread-created,id="1",group-id="i1"
72^running
(gdb)
*stopped,reason="breakpoint-hit",bkptno="1",disp="keep",frame={func="f",args=[],file="test.jam",fullname="{{.*}}test.jam",line="3"},thread-id="1",stopped-threads="all"
(gdb)
1-exec-finish
1^running
(gdb)
*stopped,reason="end-stepping-range",frame={func="g",args=[],file="test.jam",fullname="{{.*}}test.jam",line="8"},thread-id="1"
(gdb)
2-exec-finish
2^running
(gdb)
*stopped,reason="end-stepping-range",frame={func="h",args=[],file="test.jam",fullname="{{.*}}test.jam",line="14"},thread-id="1"
(gdb)
3-exec-finish
3^running
(gdb)
*stopped,reason="end-stepping-range",frame={func="module scope",args=[],file="test.jam",fullname="{{.*}}test.jam",line="21"},thread-id="1"
(gdb)
73-gdb-exit
73^exit
""")
t.cleanup()
def test_breakpoints():
"""Tests the interaction between the following commands:
break, clear, delete, disable, enable"""
t = make_tester()
t.write("test.jam", """\
rule f ( )
{
a = 1 ;
}
rule g ( )
{
b = 2 ;
}
rule h ( )
{
c = 3 ;
d = 4 ;
}
f ;
g ;
h ;
UPDATE ;
""")
run(t, """\
=thread-group-added,id="i1"
(gdb)
-break-insert f
^done,bkpt={number="1",type="breakpoint",disp="keep",enabled="y",func="f"}
(gdb)
72-exec-run -ftest.jam
=thread-created,id="1",group-id="i1"
72^running
(gdb)
*stopped,reason="breakpoint-hit",bkptno="1",disp="keep",frame={func="f",args=[],file="test.jam",fullname="{{.*}}test.jam",line="3"},thread-id="1",stopped-threads="all"
(gdb)
-interpreter-exec console kill
^done
(gdb)
-break-insert g
^done,bkpt={number="2",type="breakpoint",disp="keep",enabled="y",func="g"}
(gdb)
-break-disable 1
^done
(gdb)
73-exec-run -ftest.jam
=thread-created,id="1",group-id="i1"
73^running
(gdb)
*stopped,reason="breakpoint-hit",bkptno="2",disp="keep",frame={func="g",args=[],file="test.jam",fullname="{{.*}}test.jam",line="7"},thread-id="1",stopped-threads="all"
(gdb)
-interpreter-exec console kill
^done
(gdb)
-break-enable 1
^done
(gdb)
74-exec-run -ftest.jam
=thread-created,id="1",group-id="i1"
74^running
(gdb)
*stopped,reason="breakpoint-hit",bkptno="1",disp="keep",frame={func="f",args=[],file="test.jam",fullname="{{.*}}test.jam",line="3"},thread-id="1",stopped-threads="all"
(gdb)
-interpreter-exec console kill
^done
(gdb)
-break-delete 1
^done
(gdb)
75-exec-run -ftest.jam
=thread-created,id="1",group-id="i1"
75^running
(gdb)
*stopped,reason="breakpoint-hit",bkptno="2",disp="keep",frame={func="g",args=[],file="test.jam",fullname="{{.*}}test.jam",line="7"},thread-id="1",stopped-threads="all"
(gdb)
76-gdb-exit
76^exit
""")
t.cleanup()
test_exec_run()
test_exit_status()
test_exec_step()
test_exec_next()
test_exec_finish()
test_breakpoints()

View File

@@ -0,0 +1,674 @@
#!/usr/bin/python
# Copyright 2016 Steven Watanabe
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
# Test for the debugger
import BoostBuild
import TestCmd
import re
def split_stdin_stdout(text):
"""stdin is all text after the prompt up to and including
the next newline. Everything else is stdout. stdout
may contain regular expressions enclosed in {{}}."""
prompt = re.escape('(b2db) ')
pattern = re.compile('(?<=%s)(.*\n)' % prompt)
text = text.replace("{{bjam}}", "{{.*}}b2{{(?:\\.exe)?}}")
stdin = ''.join(re.findall(pattern, text))
stdout = re.sub(pattern, '', text)
outside_pattern = re.compile(r'(?:\A|(?<=\}\}))(?:[^\{]|(?:\{(?!\{)))*(?:(?=\{\{)|\Z)')
def escape_line(line):
line = re.sub(outside_pattern, lambda m: re.escape(m.group(0)), line)
return re.sub(r'\{\{|\}\}', '', line)
stdout = '\n'.join([escape_line(line) for line in stdout.split('\n')])
return (stdin,stdout)
def run(tester, io):
(input,output) = split_stdin_stdout(io)
tester.run_build_system(stdin=input, stdout=output, match=TestCmd.match_re)
def make_tester():
return BoostBuild.Tester(["-dconsole"], pass_toolset=False, pass_d0=False,
use_test_config=False, ignore_toolset_requirements=False, match=TestCmd.match_re)
def test_run():
t = make_tester()
t.write("test.jam", """\
UPDATE ;
""")
run(t, """\
(b2db) run -ftest.jam
Starting program: {{bjam}} -ftest.jam
Child {{\d+}} exited with status 0
(b2db) quit
""")
t.cleanup()
def test_exit_status():
t = make_tester()
t.write("test.jam", """\
EXIT : 1 ;
""")
run(t, """\
(b2db) run -ftest.jam
Starting program: {{bjam}} -ftest.jam
Child {{\d+}} exited with status 1
(b2db) quit
""")
t.cleanup()
def test_step():
t = make_tester()
t.write("test.jam", """\
rule g ( )
{
a = 1 ;
b = 2 ;
}
rule f ( )
{
g ;
c = 3 ;
}
f ;
""")
run(t, """\
(b2db) break f
Breakpoint 1 set at f
(b2db) run -ftest.jam
Starting program: {{bjam}} -ftest.jam
Breakpoint 1, f ( ) at test.jam:8
8 g ;
(b2db) step
3 a = 1 ;
(b2db) step
4 b = 2 ;
(b2db) step
9 c = 3 ;
(b2db) quit
""")
t.cleanup()
# Note: step doesn't need to worry about breakpoints,
# as it always stops at the next line executed.
def test_next():
t = make_tester()
t.write("test.jam", """\
rule g ( )
{
a = 1 ;
}
rule f ( )
{
g ;
b = 2 ;
c = 3 ;
}
rule h ( )
{
f ;
g ;
}
h ;
d = 4 ;
""")
run(t, """\
(b2db) break f
Breakpoint 1 set at f
(b2db) run -ftest.jam
Starting program: {{bjam}} -ftest.jam
Breakpoint 1, f ( ) at test.jam:7
7 g ;
(b2db) next
8 b = 2 ;
(b2db) next
9 c = 3 ;
(b2db) next
14 g ;
(b2db) next
17 d = 4 ;
(b2db) quit
""")
t.cleanup()
def test_next_breakpoint():
"""next should stop if it encounters a breakpoint.
If the normal end point happens to be a breakpoint,
then it should be reported as normal stepping."""
t = make_tester()
t.write("test.jam", """\
rule f ( recurse ? )
{
if $(recurse) { f ; }
a = 1 ;
}
rule g ( )
{
b = 2 ;
}
f true ;
g ;
""")
run(t, """\
(b2db) break f
Breakpoint 1 set at f
(b2db) break g
Breakpoint 2 set at g
(b2db) break test.jam:4
Breakpoint 3 set at test.jam:4
(b2db) run -ftest.jam
Starting program: {{bjam}} -ftest.jam
Breakpoint 1, f ( true ) at test.jam:3
3 if $(recurse) { f ; }
(b2db) next
Breakpoint 1, f ( ) at test.jam:3
3 if $(recurse) { f ; }
(b2db) next
4 a = 1 ;
(b2db) next
4 a = 1 ;
(b2db) next
11 g ;
(b2db) next
Breakpoint 2, g ( ) at test.jam:8
8 b = 2 ;
(b2db) quit
""")
t.cleanup()
def test_finish():
t = make_tester()
t.write("test.jam", """\
rule f ( )
{
a = 1 ;
}
rule g ( )
{
f ;
b = 2 ;
i ;
}
rule h ( )
{
g ;
i ;
}
rule i ( )
{
c = 3 ;
}
h ;
d = 4 ;
""")
run(t, """\
(b2db) break f
Breakpoint 1 set at f
(b2db) run -ftest.jam
Starting program: {{bjam}} -ftest.jam
Breakpoint 1, f ( ) at test.jam:3
3 a = 1 ;
(b2db) finish
8 b = 2 ;
(b2db) finish
14 i ;
(b2db) finish
21 d = 4 ;
(b2db) quit
""")
t.cleanup()
def test_finish_breakpoints():
"""finish should stop when it reaches a breakpoint."""
t = make_tester()
t.write("test.jam", """\
rule f ( recurse * )
{
if $(recurse)
{
a = [ f $(recurse[2-]) ] ;
}
}
rule g ( list * )
{
for local v in $(list)
{
x = $(v) ;
}
}
f 1 2 ;
g 1 2 ;
""")
run(t, """\
(b2db) break test.jam:5
Breakpoint 1 set at test.jam:5
(b2db) break test.jam:12
Breakpoint 2 set at test.jam:12
(b2db) run -ftest.jam
Starting program: {{bjam}} -ftest.jam
Breakpoint 1, f ( 1 2 ) at test.jam:5
5 a = [ f $(recurse[2-]) ] ;
(b2db) finish
Breakpoint 1, f ( 2 ) at test.jam:5
5 a = [ f $(recurse[2-]) ] ;
(b2db) finish
5 a = [ f $(recurse[2-]) ] ;
(b2db) finish
16 g 1 2 ;
(b2db) finish
Breakpoint 2, g ( 1 2 ) at test.jam:12
12 x = $(v) ;
(b2db) finish
Breakpoint 2, g ( 1 2 ) at test.jam:12
12 x = $(v) ;
(b2db) quit
""")
t.cleanup()
def test_continue_breakpoints():
"""continue should stop when it reaches a breakpoint"""
t = make_tester()
t.write("test.jam", """\
rule f ( recurse * )
{
if $(recurse)
{
a = [ f $(recurse[2-]) ] ;
}
}
rule g ( list * )
{
for local v in $(list)
{
x = $(v) ;
}
}
f 1 2 ;
g 1 2 ;
""")
run(t, """\
(b2db) break test.jam:5
Breakpoint 1 set at test.jam:5
(b2db) break test.jam:12
Breakpoint 2 set at test.jam:12
(b2db) run -ftest.jam
Starting program: {{bjam}} -ftest.jam
Breakpoint 1, f ( 1 2 ) at test.jam:5
5 a = [ f $(recurse[2-]) ] ;
(b2db) continue
Breakpoint 1, f ( 2 ) at test.jam:5
5 a = [ f $(recurse[2-]) ] ;
(b2db) continue
Breakpoint 1, f ( 1 2 ) at test.jam:5
5 a = [ f $(recurse[2-]) ] ;
(b2db) continue
Breakpoint 2, g ( 1 2 ) at test.jam:12
12 x = $(v) ;
(b2db) continue
Breakpoint 2, g ( 1 2 ) at test.jam:12
12 x = $(v) ;
(b2db) quit
""")
t.cleanup()
def test_breakpoints():
"""Tests the interaction between the following commands:
break, clear, delete, disable, enable"""
t = make_tester()
t.write("test.jam", """\
rule f ( )
{
a = 1 ;
}
rule g ( )
{
b = 2 ;
}
rule h ( )
{
c = 3 ;
d = 4 ;
}
f ;
g ;
h ;
UPDATE ;
""")
run(t, """\
(b2db) break f
Breakpoint 1 set at f
(b2db) run -ftest.jam
Starting program: {{bjam}} -ftest.jam
Breakpoint 1, f ( ) at test.jam:3
3 a = 1 ;
(b2db) kill
(b2db) break g
Breakpoint 2 set at g
(b2db) disable 1
(b2db) run -ftest.jam
Starting program: {{bjam}} -ftest.jam
Breakpoint 2, g ( ) at test.jam:7
7 b = 2 ;
(b2db) kill
(b2db) enable 1
(b2db) run -ftest.jam
Starting program: {{bjam}} -ftest.jam
Breakpoint 1, f ( ) at test.jam:3
3 a = 1 ;
(b2db) kill
(b2db) delete 1
(b2db) run -ftest.jam
Starting program: {{bjam}} -ftest.jam
Breakpoint 2, g ( ) at test.jam:7
7 b = 2 ;
(b2db) kill
(b2db) break test.jam:12
Breakpoint 3 set at test.jam:12
(b2db) clear g
Deleted breakpoint 2
(b2db) run -ftest.jam
Starting program: {{bjam}} -ftest.jam
Breakpoint 3, h ( ) at test.jam:12
12 d = 4 ;
(b2db) kill
(b2db) clear test.jam:12
Deleted breakpoint 3
(b2db) run -ftest.jam
Starting program: {{bjam}} -ftest.jam
Child {{\d+}} exited with status 0
(b2db) quit
""")
t.cleanup()
def test_breakpoints_running():
"""Tests that breakpoints can be added and modified
while the program is running."""
t = make_tester()
t.write("test.jam", """\
rule f ( )
{
a = 1 ;
}
rule g ( )
{
b = 2 ;
}
rule h ( )
{
c = 3 ;
d = 4 ;
}
f ;
g ;
h ;
UPDATE ;
""")
run(t, """\
(b2db) break test.jam:14
Breakpoint 1 set at test.jam:14
(b2db) run -ftest.jam
Starting program: {{bjam}} -ftest.jam
Breakpoint 1, module scope at test.jam:14
14 f ;
(b2db) break f
Breakpoint 2 set at f
(b2db) continue
Breakpoint 2, f ( ) at test.jam:3
3 a = 1 ;
(b2db) kill
(b2db) run -ftest.jam
Starting program: {{bjam}} -ftest.jam
Breakpoint 1, module scope at test.jam:14
14 f ;
(b2db) break g
Breakpoint 3 set at g
(b2db) disable 2
(b2db) continue
Breakpoint 3, g ( ) at test.jam:7
7 b = 2 ;
(b2db) kill
(b2db) run -ftest.jam
Starting program: {{bjam}} -ftest.jam
Breakpoint 1, module scope at test.jam:14
14 f ;
(b2db) enable 2
(b2db) continue
Breakpoint 2, f ( ) at test.jam:3
3 a = 1 ;
(b2db) kill
(b2db) run -ftest.jam
Starting program: {{bjam}} -ftest.jam
Breakpoint 1, module scope at test.jam:14
14 f ;
(b2db) delete 2
(b2db) continue
Breakpoint 3, g ( ) at test.jam:7
7 b = 2 ;
(b2db) kill
(b2db) run -ftest.jam
Starting program: {{bjam}} -ftest.jam
Breakpoint 1, module scope at test.jam:14
14 f ;
(b2db) break test.jam:12
Breakpoint 4 set at test.jam:12
(b2db) clear g
Deleted breakpoint 3
(b2db) continue
Breakpoint 4, h ( ) at test.jam:12
12 d = 4 ;
(b2db) kill
(b2db) run -ftest.jam
Starting program: {{bjam}} -ftest.jam
Breakpoint 1, module scope at test.jam:14
14 f ;
(b2db) clear test.jam:12
Deleted breakpoint 4
(b2db) continue
Child {{\d+}} exited with status 0
(b2db) quit
""")
t.cleanup()
def test_backtrace():
t = make_tester()
t.write("test.jam", """\
rule f ( x * : y * : z * )
{
return $(x) ;
}
rule g ( x * : y * : z * )
{
return [ f $(x) : $(y) : $(z) ] ;
}
g 1 : 2 : 3 ;
""")
run(t, """\
(b2db) break f
Breakpoint 1 set at f
(b2db) run -ftest.jam
Starting program: {{bjam}} -ftest.jam
Breakpoint 1, f ( 1 : 2 : 3 ) at test.jam:3
3 return $(x) ;
(b2db) backtrace
#0 in f ( 1 : 2 : 3 ) at test.jam:3
#1 in g ( 1 : 2 : 3 ) at test.jam:7
#2 in module scope at test.jam:9
(b2db) quit
""")
t.cleanup()
def test_print():
t = make_tester()
t.write("test.jam", """\
rule f ( args * )
{
return $(args) ;
}
f x ;
f x y ;
""")
run(t, """\
(b2db) break f
Breakpoint 1 set at f
(b2db) run -ftest.jam
Starting program: {{bjam}} -ftest.jam
Breakpoint 1, f ( x ) at test.jam:3
3 return $(args) ;
(b2db) print $(args)
x
(b2db) continue
Breakpoint 1, f ( x y ) at test.jam:3
3 return $(args) ;
(b2db) print $(args)
x y
(b2db) disable 1
(b2db) print [ f z ]
z
(b2db) quit
""")
t.cleanup()
def test_run_running():
t = make_tester()
t.write("test.jam", """\
UPDATE ;
""")
run(t, """\
(b2db) break test.jam:1
Breakpoint 1 set at test.jam:1
(b2db) run -ftest.jam
Starting program: {{bjam}} -ftest.jam
Breakpoint 1, module scope at test.jam:1
1 UPDATE ;
(b2db) run -ftest.jam
Child {{\d+}} exited with status 0
Starting program: {{bjam}} -ftest.jam
Breakpoint 1, module scope at test.jam:1
1 UPDATE ;
(b2db) quit
""")
t.cleanup()
def test_error_not_running():
t = make_tester()
run(t, """\
(b2db) continue
The program is not being run.
(b2db) step
The program is not being run.
(b2db) next
The program is not being run.
(b2db) finish
The program is not being run.
(b2db) kill
The program is not being run.
(b2db) backtrace
The program is not being run.
(b2db) print 1
The program is not being run.
(b2db) quit
""")
t.cleanup()
def test_bad_arguments():
t = make_tester()
t.write("test.jam", """\
UPDATE ;
""")
run(t, """\
(b2db) break test.jam:1
Breakpoint 1 set at test.jam:1
(b2db) run -ftest.jam
Starting program: {{bjam}} -ftest.jam
Breakpoint 1, module scope at test.jam:1
1 UPDATE ;
(b2db) continue 1
Too many arguments to continue.
(b2db) step 1
Too many arguments to step.
(b2db) next 1
Too many arguments to next.
(b2db) finish 1
Too many arguments to finish.
(b2db) break
Missing argument to break.
(b2db) break x y
Too many arguments to break.
(b2db) disable
Missing argument to disable.
(b2db) disable 1 2
Too many arguments to disable.
(b2db) disable x
Invalid breakpoint number x.
(b2db) disable 2
Unknown breakpoint 2.
(b2db) enable
Missing argument to enable.
(b2db) enable 1 2
Too many arguments to enable.
(b2db) enable x
Invalid breakpoint number x.
(b2db) enable 2
Unknown breakpoint 2.
(b2db) delete
Missing argument to delete.
(b2db) delete 1 2
Too many arguments to delete.
(b2db) delete x
Invalid breakpoint number x.
(b2db) delete 2
Unknown breakpoint 2.
(b2db) clear
Missing argument to clear.
(b2db) clear test.jam:1 test.jam:1
Too many arguments to clear.
(b2db) clear test.jam:2
No breakpoint at test.jam:2.
(b2db) quit
""")
t.cleanup()
def test_unknown_command():
t = make_tester()
run(t, """\
(b2db) xyzzy
Unknown command: xyzzy
(b2db) gnusto rezrov
Unknown command: gnusto
(b2db) quit
""")
t.cleanup()
test_run()
test_exit_status()
test_step()
test_next()
test_next_breakpoint()
test_finish()
test_finish_breakpoints()
test_continue_breakpoints()
test_breakpoints()
test_breakpoints_running()
test_backtrace()
test_print()
test_run_running()
test_error_not_running()
test_bad_arguments()
test_unknown_command()

View File

@@ -0,0 +1,81 @@
#!/usr/bin/python
# Copyright 2003 Dave Abrahams
# Copyright 2002, 2003 Vladimir Prus
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
# Test that default build clause actually has any effect.
import BoostBuild
t = BoostBuild.Tester(use_test_config=False)
t.write("jamroot.jam", "")
t.write("jamfile.jam", "exe a : a.cpp : : debug release ;")
t.write("a.cpp", "int main() {}\n")
t.run_build_system()
t.expect_addition("bin/$toolset/debug*/a.exe")
t.expect_addition("bin/$toolset/release*/a.exe")
# Check that explictly-specified build variant suppresses default-build.
t.rm("bin")
t.run_build_system(["release"])
t.expect_addition(BoostBuild.List("bin/$toolset/release*/") * "a.exe a.obj")
t.ignore_addition('bin/*/a.rsp')
t.expect_nothing_more()
# Now check that we can specify explicit build request and default-build will be
# combined with it.
t.run_build_system(["optimization=space"])
t.expect_addition("bin/$toolset/debug/optimization-space*/a.exe")
t.expect_addition("bin/$toolset/release/optimization-space*/a.exe")
# Test that default-build must be identical in all alternatives. Error case.
t.write("jamfile.jam", """\
exe a : a.cpp : : debug ;
exe a : b.cpp : : ;
""")
t.run_build_system(["-n", "--no-error-backtrace"], status=1)
t.fail_test(t.stdout().find("default build must be identical in all alternatives") == -1)
# Test that default-build must be identical in all alternatives. No Error case,
# empty default build.
t.write("jamfile.jam", """\
exe a : a.cpp : <variant>debug ;
exe a : b.cpp : <variant>release ;
""")
t.run_build_system(["-n", "--no-error-backtrace"], status=0)
# Now try a harder example: default build which contains <define> should cause
# <define> to be present when "b" is compiled. This happens only if
# "build-project b" is placed first.
t.write("jamfile.jam", """\
project : default-build <define>FOO ;
build-project a ;
build-project b ;
""")
t.write("a/jamfile.jam", "exe a : a.cpp ../b//b ;")
t.write("a/a.cpp", """\
#ifdef _WIN32
__declspec(dllimport)
#endif
void foo();
int main() { foo(); }
""")
t.write("b/jamfile.jam", "lib b : b.cpp ;")
t.write("b/b.cpp", """\
#ifdef FOO
#ifdef _WIN32
__declspec(dllexport)
#endif
void foo() {}
#endif
""")
t.run_build_system()
t.cleanup()

View File

@@ -0,0 +1,50 @@
#!/usr/bin/python
# Copyright 2003 Vladimir Prus
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
# Test that features with default values are always present in build properties
# of any target.
import BoostBuild
t = BoostBuild.Tester(use_test_config=False)
# Declare *non-propagated* feature foo.
t.write("jamroot.jam", """
import feature : feature ;
feature foo : on off ;
""")
# Note that '<foo>on' will not be propagated to 'd/l'.
t.write("jamfile.jam", """
exe hello : hello.cpp d//l ;
""")
t.write("hello.cpp", """
#ifdef _WIN32
__declspec(dllimport)
#endif
void foo();
int main() { foo(); }
""")
t.write("d/jamfile.jam", """
lib l : l.cpp : <foo>on:<define>FOO ;
""")
t.write("d/l.cpp", """
#ifdef _WIN32
__declspec(dllexport)
#endif
#ifdef FOO
void foo() {}
#endif
""")
t.run_build_system()
t.expect_addition("bin/$toolset/debug*/hello.exe")
t.cleanup()

View File

@@ -0,0 +1,215 @@
#!/usr/bin/python
# Copyright 2008 Jurko Gospodnetic
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
# Test that the expected default toolset is used when no toolset is explicitly
# specified on the command line or used from code via the using rule. Test that
# the default toolset is correctly used just like any other explicitly used
# toolset (e.g. toolset prerequisites, properties conditioned on toolset
# related features, etc.).
#
# Note that we need to ignore regular site/user/test configuration files to
# avoid them marking any toolsets not under our control as used.
import BoostBuild
# Line displayed by Boost Build when using the default toolset.
configuring_default_toolset_message = \
'warning: Configuring default toolset "%s".'
###############################################################################
#
# test_conditions_on_default_toolset()
# ------------------------------------
#
###############################################################################
def test_conditions_on_default_toolset():
"""Test that toolset and toolset subfeature conditioned properties get
applied correctly when the toolset is selected by default. Implicitly tests
that we can use the set-default-toolset rule to set the default toolset to
be used by Boost Build.
"""
t = BoostBuild.Tester("--user-config= --ignore-site-config",
pass_toolset=False, use_test_config=False)
toolset_name = "myCustomTestToolset"
toolset_version = "v"
toolset_version_unused = "v_unused"
message_loaded = "Toolset '%s' loaded." % toolset_name
message_initialized = "Toolset '%s' initialized." % toolset_name ;
# Custom toolset.
t.write(toolset_name + ".jam", """
import feature ;
ECHO "%(message_loaded)s" ;
feature.extend toolset : %(toolset_name)s ;
feature.subfeature toolset %(toolset_name)s : version : %(toolset_version)s %(toolset_version_unused)s ;
rule init ( version ) { ECHO "%(message_initialized)s" ; }
""" % {'message_loaded' : message_loaded ,
'message_initialized' : message_initialized,
'toolset_name' : toolset_name ,
'toolset_version' : toolset_version ,
'toolset_version_unused': toolset_version_unused})
# Main Boost Build project script.
t.write("jamroot.jam", """
import build-system ;
import errors ;
import feature ;
import notfile ;
build-system.set-default-toolset %(toolset_name)s : %(toolset_version)s ;
feature.feature description : : free incidental ;
# We use a rule instead of an action to avoid problems with action output not
# getting piped to stdout by the testing system.
rule buildRule ( names : targets ? : properties * )
{
local descriptions = [ feature.get-values description : $(properties) ] ;
ECHO "descriptions:" /$(descriptions)/ ;
local toolset = [ feature.get-values toolset : $(properties) ] ;
ECHO "toolset:" /$(toolset)/ ;
local toolset-version = [ feature.get-values "toolset-$(toolset):version" : $(properties) ] ;
ECHO "toolset-version:" /$(toolset-version)/ ;
}
notfile testTarget
: @buildRule
:
:
<description>stand-alone
<toolset>%(toolset_name)s:<description>toolset
<toolset>%(toolset_name)s-%(toolset_version)s:<description>toolset-version
<toolset>%(toolset_name)s-%(toolset_version_unused)s:<description>toolset-version-unused ;
""" % {'toolset_name' : toolset_name ,
'toolset_version' : toolset_version,
'toolset_version_unused': toolset_version_unused})
t.run_build_system()
t.expect_output_lines(configuring_default_toolset_message % toolset_name)
t.expect_output_lines(message_loaded)
t.expect_output_lines(message_initialized)
t.expect_output_lines("descriptions: /stand-alone/ /toolset/ "
"/toolset-version/")
t.expect_output_lines("toolset: /%s/" % toolset_name)
t.expect_output_lines("toolset-version: /%s/" % toolset_version)
t.cleanup()
###############################################################################
#
# test_default_toolset_on_os()
# ----------------------------
#
###############################################################################
def test_default_toolset_on_os( os, expected_toolset ):
"""Test that the given toolset is used as the default toolset on the given
os. Uses hardcoded knowledge of how Boost Build decides on which host OS it
is currently running. Note that we must not do much after tricking Boost
Build into believing it has a specific host OS as this might mess up other
important internal Boost Build state.
"""
t = BoostBuild.Tester("--user-config= --ignore-site-config",
pass_toolset=False, use_test_config=False)
t.write("jamroot.jam", "modules.poke os : .name : %s ;" % os)
# We need to tell the test system to ignore stderr output as attempting to
# load missing toolsets might cause random failures with which we are not
# concerned in this test.
t.run_build_system(stderr=None)
t.expect_output_lines(configuring_default_toolset_message %
expected_toolset)
t.cleanup()
###############################################################################
#
# test_default_toolset_requirements()
# -----------------------------------
#
###############################################################################
def test_default_toolset_requirements():
"""Test that default toolset's requirements get applied correctly.
"""
t = BoostBuild.Tester("--user-config= --ignore-site-config",
pass_toolset=False, use_test_config=False,
ignore_toolset_requirements=False)
toolset_name = "customTestToolsetWithRequirements"
# Custom toolset.
t.write(toolset_name + ".jam", """
import feature ;
import toolset ;
feature.extend toolset : %(toolset_name)s ;
toolset.add-requirements <description>toolset-requirement ;
rule init ( ) { }
""" % {'toolset_name': toolset_name})
# Main Boost Build project script.
t.write("jamroot.jam", """
import build-system ;
import errors ;
import feature ;
import notfile ;
build-system.set-default-toolset %(toolset_name)s ;
feature.feature description : : free incidental ;
# We use a rule instead of an action to avoid problems with action output not
# getting piped to stdout by the testing system.
rule buildRule ( names : targets ? : properties * )
{
local descriptions = [ feature.get-values description : $(properties) ] ;
ECHO "descriptions:" /$(descriptions)/ ;
local toolset = [ feature.get-values toolset : $(properties) ] ;
ECHO "toolset:" /$(toolset)/ ;
}
notfile testTarget
: @buildRule
:
:
<description>target-requirement
<description>toolset-requirement:<description>conditioned-requirement
<description>unrelated-condition:<description>unrelated-description ;
""" % {'toolset_name': toolset_name})
t.run_build_system()
t.expect_output_lines(configuring_default_toolset_message % toolset_name)
t.expect_output_lines("descriptions: /conditioned-requirement/ "
"/target-requirement/ /toolset-requirement/")
t.expect_output_lines("toolset: /%s/" % toolset_name)
t.cleanup()
###############################################################################
#
# main()
# ------
#
###############################################################################
test_default_toolset_on_os("NT" , "msvc")
test_default_toolset_on_os("LINUX" , "gcc" )
test_default_toolset_on_os("CYGWIN" , "gcc" )
test_default_toolset_on_os("SomeOtherOS", "gcc" )
test_default_toolset_requirements()
test_conditions_on_default_toolset()

View File

@@ -0,0 +1,38 @@
#!/usr/bin/python
# Copyright 2003 Vladimir Prus
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
# Regression test: virtual targets with different dependency properties were
# considered different by 'virtual-target.register', but the code which
# determined the actual target paths ignored dependency properties so both
# targets ended up being in the same location.
#
# This test has flip-flopped several times between passing and failing.
# Currently, the library is only considered relevant for linking and thus
# does not cause a conflict. SJW 20180115
import BoostBuild
t = BoostBuild.Tester()
t.write("jamroot.jam", """\
lib foo : foo.cpp ;
exe hello : hello.cpp ;
exe hello2 : hello.cpp : <library>foo ;
""")
t.write("hello.cpp", "int main() {}\n")
t.write("foo.cpp", """\
#ifdef _WIN32
__declspec(dllexport)
#endif
void foo() {}
""")
t.run_build_system(["--no-error-backtrace"], status=0)
t.cleanup()

View File

@@ -0,0 +1,243 @@
#!/usr/bin/python
# Copyright 2003 Dave Abrahams
# Copyright 2002, 2003, 2005, 2006 Vladimir Prus
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
import BoostBuild
def test_basic():
t = BoostBuild.Tester(["-d3", "-d+12"], use_test_config=False)
t.write("a.cpp", """
#include <a.h>
# include "a.h"
#include <x.h>
int main() {}
""")
t.write("a.h", "\n")
t.write("a_c.c", """\
#include <a.h>
# include "a.h"
#include <x.h>
""")
t.write("b.cpp", """\
#include "a.h"
int main() {}
""")
t.write("b.h", "\n")
t.write("c.cpp", """\
#include "x.h"
int main() {}
""")
t.write("e.cpp", """\
#include "x.h"
int main() {}
""")
t.write("x.foo", "")
t.write("y.foo", "")
t.write("src1/a.h", '#include "b.h"\n')
t.write("src1/b.h", '#include "c.h"\n')
t.write("src1/c.h", "\n")
t.write("src1/z.h", """\
extern int dummy_variable_suppressing_empty_file_warning_on_hp_cxx_compiler;
""")
t.write("src2/b.h", "\n")
t.write("jamroot.jam", """\
import foo ;
import types/cpp ;
import types/exe ;
project test : requirements <include>src1 ;
exe a : x.foo a.cpp a_c.c ;
exe b : b.cpp ;
# Because of <define>FOO, c.cpp will be compiled to a different directory than
# everything for main target "a". Therefore, without <implicit-dependency>, C
# preprocessor processing that module will not find "x.h", which is part of
# "a"'s dependency graph.
#
# --------------------------
# More detailed explanation:
# --------------------------
# c.cpp includes x.h which does not exist on the current include path so Boost
# Jam will try to match it to existing Jam targets to cover cases as this one
# where the file is generated by the same build.
#
# However, as x.h is not part of "c" metatarget's dependency graph, Boost
# Build will not actualize its target by default, i.e. create its Jam target.
#
# To get the Jam target created in time, we use the <implicit-dependency>
# feature. This tells Boost Build that it needs to actualize the dependency
# graph for metatarget "a", even though that metatarget has not been directly
# mentioned and is not a dependency for any of the metatargets mentioned in the
# current build request.
#
# Note that Boost Build does not automatically add a dependency between the
# Jam targets in question so, if Boost Jam does not add a dependency on a target
# from that other dependency graph (x.h in our case), i.e. if c.cpp does not
# actually include x.h, us actualizing it will have no effect in the end as
# Boost Jam will not have a reason to actually build those targets in spite of
# knowing about them.
exe c : c.cpp : <define>FOO <implicit-dependency>a ;
""")
t.write("foo.jam", """\
import generators ;
import modules ;
import os ;
import print ;
import type ;
import types/cpp ;
type.register FOO : foo ;
generators.register-standard foo.foo : FOO : CPP H ;
nl = "
" ;
rule foo ( targets * : sources * : properties * )
{
# On NT, you need an exported symbol in order to have an import library
# generated. We will not really use the symbol defined here, just force the
# import library creation.
if ( [ os.name ] = NT || [ modules.peek : OS ] in CYGWIN ) &&
<main-target-type>LIB in $(properties)
{
.decl = "void __declspec(dllexport) foo() {}" ;
}
print.output $(<[1]) ;
print.text $(.decl:E="//")$(nl) ;
print.output $(<[2]) ;
print.text "#include <z.h>"$(nl) ;
}
""")
t.write("foo.py",
r"""import bjam
import b2.build.type as type
import b2.build.generators as generators
from b2.manager import get_manager
type.register("FOO", ["foo"])
generators.register_standard("foo.foo", ["FOO"], ["CPP", "H"])
def prepare_foo(targets, sources, properties):
if properties.get('os') in ['windows', 'cygwin']:
bjam.call('set-target-variable', targets, "DECL",
"void __declspec(dllexport) foo() {}")
get_manager().engine().register_action("foo.foo",
"echo -e $(DECL:E=//)\\n > $(<[1])\n"
"echo -e "#include <z.h>\\n" > $(<[2])\n", function=prepare_foo)
""")
# Check that main target 'c' was able to find 'x.h' from 'a's dependency
# graph.
t.run_build_system()
t.expect_addition("bin/$toolset/debug*/c.exe")
# Check handling of first level includes.
# Both 'a' and 'b' include "a.h" and should be updated.
t.touch("a.h")
t.run_build_system()
t.expect_touch("bin/$toolset/debug*/a.exe")
t.expect_touch("bin/$toolset/debug*/a.obj")
t.expect_touch("bin/$toolset/debug*/a_c.obj")
t.expect_touch("bin/$toolset/debug*/b.exe")
t.expect_touch("bin/$toolset/debug*/b.obj")
t.ignore_touch("bin/*/a.rsp")
t.ignore_touch("bin/*/b.rsp")
t.expect_nothing_more()
# Only source files using include <a.h> should be compiled.
t.touch("src1/a.h")
t.run_build_system()
t.expect_touch("bin/$toolset/debug*/a.exe")
t.expect_touch("bin/$toolset/debug*/a.obj")
t.expect_touch("bin/$toolset/debug*/a_c.obj")
t.ignore_touch("bin/*/a.rsp")
t.expect_nothing_more()
# "src/a.h" includes "b.h" (in the same dir).
t.touch("src1/b.h")
t.run_build_system()
t.expect_touch("bin/$toolset/debug*/a.exe")
t.expect_touch("bin/$toolset/debug*/a.obj")
t.expect_touch("bin/$toolset/debug*/a_c.obj")
t.ignore_touch("bin/*/a.rsp")
t.expect_nothing_more()
# Included by "src/b.h". We had a bug: file included using double quotes
# (e.g. "b.h") was not scanned at all in this case.
t.touch("src1/c.h")
t.run_build_system()
t.expect_touch("bin/$toolset/debug*/a.exe")
t.touch("b.h")
t.run_build_system()
t.expect_nothing_more()
# Test dependency on a generated header.
#
# TODO: we have also to check that generated header is found correctly if
# it is different for different subvariants. Lacking any toolset support,
# this check will be implemented later.
t.touch("x.foo")
t.run_build_system()
t.expect_touch("bin/$toolset/debug*/a.obj")
t.expect_touch("bin/$toolset/debug*/a_c.obj")
# Check that generated headers are scanned for dependencies as well.
t.touch("src1/z.h")
t.run_build_system()
t.expect_touch("bin/$toolset/debug*/a.obj")
t.expect_touch("bin/$toolset/debug*/a_c.obj")
t.cleanup()
def test_scanned_includes_with_absolute_paths():
"""
Regression test: on Windows, <includes> with absolute paths were not
considered when scanning dependencies.
"""
t = BoostBuild.Tester(["-d3", "-d+12"])
t.write("jamroot.jam", """\
path-constant TOP : . ;
exe app : main.cpp : <include>$(TOP)/include ;
""");
t.write("main.cpp", """\
#include <dir/header.h>
int main() {}
""")
t.write("include/dir/header.h", "\n")
t.run_build_system()
t.expect_addition("bin/$toolset/debug*/main.obj")
t.touch("include/dir/header.h")
t.run_build_system()
t.expect_touch("bin/$toolset/debug*/main.obj")
t.cleanup()
test_basic()
test_scanned_includes_with_absolute_paths()

View File

@@ -0,0 +1,32 @@
#!/usr/bin/python
# Copyright (C) Vladimir Prus 2006.
# Distributed under the Boost Software License, Version 1.0. (See
# accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
# Test that it is possible to add a suffix to a main target name to disambiguate
# that main target from another, and that this does not affect the names of the
# generated targets.
import BoostBuild
t = BoostBuild.Tester(use_test_config=False)
t.write("jamroot.jam", """
exe hello.exe : hello.obj ;
obj hello.obj : hello.cpp : <variant>debug ;
obj hello.obj2 : hello.cpp : <variant>release ;
""")
t.write("hello.cpp", """
int main() {}
""")
t.run_build_system()
t.expect_addition("bin/$toolset/debug*/hello.exe")
t.expect_addition("bin/$toolset/debug*/hello.obj")
t.expect_addition("bin/$toolset/release*/hello.obj")
t.cleanup()

View File

@@ -0,0 +1,163 @@
#!/usr/bin/python
# Copyright (C) 2003. Vladimir Prus
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
# Test that the <dll-path> property is correctly set when using
# <hardcode-dll-paths>true.
import BoostBuild
t = BoostBuild.Tester(use_test_config=False)
# The point of this test is to have exe "main" which uses library "b", which
# uses library "a". When "main" is built with <hardcode-dll-paths>true, paths
# to both libraries should be present as values of <dll-path> feature. We
# create a special target type which reports <dll-path> values on its sources
# and compare the list of found values with out expectations.
t.write("jamroot.jam", "using dll_paths ;")
t.write("jamfile.jam", """\
exe main : main.cpp b//b ;
explicit main ;
path-list mp : main ;
""")
t.write("main.cpp", "int main() {}\n")
t.write("dll_paths.jam", """\
import "class" : new ;
import feature ;
import generators ;
import print ;
import sequence ;
import type ;
rule init ( )
{
type.register PATH_LIST : pathlist ;
class dll-paths-list-generator : generator
{
rule __init__ ( )
{
generator.__init__ dll_paths.list : EXE : PATH_LIST ;
}
rule generated-targets ( sources + : property-set : project name ? )
{
local dll-paths ;
for local s in $(sources)
{
local a = [ $(s).action ] ;
if $(a)
{
local p = [ $(a).properties ] ;
dll-paths += [ $(p).get <dll-path> ] ;
}
}
return [ generator.generated-targets $(sources) :
[ $(property-set).add-raw $(dll-paths:G=<dll-path>) ] :
$(project) $(name) ] ;
}
}
generators.register [ new dll-paths-list-generator ] ;
}
rule list ( target : sources * : properties * )
{
local paths = [ feature.get-values <dll-path> : $(properties) ] ;
paths = [ sequence.insertion-sort $(paths) ] ;
print.output $(target) ;
print.text $(paths) ;
}
""")
t.write("dll_paths.py", """\
import bjam
import b2.build.type as type
import b2.build.generators as generators
from b2.manager import get_manager
def init():
type.register("PATH_LIST", ["pathlist"])
class DllPathsListGenerator(generators.Generator):
def __init__(self):
generators.Generator.__init__(self, "dll_paths.list", False,
["EXE"], ["PATH_LIST"])
def generated_targets(self, sources, ps, project, name):
dll_paths = []
for s in sources:
a = s.action()
if a:
p = a.properties()
dll_paths += p.get('dll-path')
dll_paths.sort()
return generators.Generator.generated_targets(self, sources,
ps.add_raw(["<dll-path>" + p for p in dll_paths]), project,
name)
generators.register(DllPathsListGenerator())
command = \"\"\"
echo $(PATHS) > $(<[1])
\"\"\"
def function(target, sources, ps):
bjam.call('set-target-variable', target, "PATHS", ps.get('dll-path'))
get_manager().engine().register_action("dll_paths.list", command,
function=function)
""")
t.write("a/jamfile.jam", "lib a : a.cpp ;")
t.write("a/a.cpp", """\
void
#if defined(_WIN32)
__declspec(dllexport)
#endif
foo() {}
""")
t.write("b/jamfile.jam", "lib b : b.cpp ../a//a ;")
t.write("b/b.cpp", """\
void
#if defined(_WIN32)
__declspec(dllexport)
#endif
bar() {}
""")
t.run_build_system(["hardcode-dll-paths=true"])
t.expect_addition("bin/$toolset/debug*/mp.pathlist")
es1 = t.adjust_name("a/bin/$toolset/debug*")
es2 = t.adjust_name("b/bin/$toolset/debug*")
t.expect_content_lines("bin/$toolset/debug*/mp.pathlist", "*" + es1)
t.expect_content_lines("bin/$toolset/debug*/mp.pathlist", "*" + es2)
t.rm("bin/$toolset/debug*/mp.pathlist")
# Now run the same checks with pre-built libraries
adll = t.glob_file("a/bin/$toolset/debug*/a.dll")
bdll = t.glob_file("b/bin/$toolset/debug*/b.dll")
t.write("b/jamfile.jam", """
local bdll = %s ;
# Make sure that it is found even with multiple source-locations
project : source-location c $(bdll:D) ;
lib b : ../a//a : <file>$(bdll:D=) ;
""" % bdll.replace("\\", "\\\\"))
t.run_build_system(["hardcode-dll-paths=true"])
t.expect_addition("bin/$toolset/debug*/mp.pathlist")
t.expect_content_lines("bin/$toolset/debug*/mp.pathlist", "*" + es1)
t.expect_content_lines("bin/$toolset/debug*/mp.pathlist", "*" + es2)
t.cleanup()

View File

@@ -0,0 +1,31 @@
#!/usr/bin/python
# Copyright 2003 Vladimir Prus
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
import BoostBuild
t = BoostBuild.Tester()
# Regression test for double loading of the same Jamfile.
t.write("jamroot.jam", "")
t.write("jamfile.jam", "build-project subdir ;")
t.write("subdir/jamfile.jam", 'ECHO "Loaded subdir" ;')
t.run_build_system(subdir="subdir")
t.expect_output_lines("Loaded subdir")
# Regression test for a more contrived case. The top-level Jamfile refers to
# subdir via use-project, while subdir's Jamfile is being loaded. The
# motivation why use-project referring to subprojects is useful can be found
# at: http://article.gmane.org/gmane.comp.lib.boost.build/3906
t.write("jamroot.jam", "")
t.write("jamfile.jam", "use-project /subdir : subdir ;")
t.write("subdir/jamfile.jam", "project subdir ;")
t.run_build_system(subdir="subdir");
t.cleanup()

View File

@@ -0,0 +1,38 @@
#!/usr/bin/python
# Copyright 2004 Vladimir Prus
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
# This test tries to stage the same file to the same location by *two* different
# stage rules, in two different projects. This is not exactly good thing to do,
# but still, V2 should handle this. We had two bugs:
# - since the file is referred from two projects, we created to different
# virtual targets
# - we also failed to figure out that the two target corresponding to the copied
# files (created in two projects) are actually equivalent.
import BoostBuild
t = BoostBuild.Tester()
t.write("a.cpp", """
""")
t.write("jamroot.jam", """
build-project a ;
build-project b ;
""")
t.write("a/jamfile.jam", """
stage bin : ../a.cpp : <location>../dist ;
""")
t.write("b/jamfile.jam", """
stage bin : ../a.cpp : <location>../dist ;
""")
t.run_build_system()
t.expect_addition("dist/a.cpp")
t.cleanup()

View File

@@ -0,0 +1,21 @@
#!/usr/bin/python
# Copyright (C) Vladimir Prus 2006.
# Distributed under the Boost Software License, Version 1.0. (See
# accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
# Test the 'customization' example.
import BoostBuild
t = BoostBuild.Tester()
t.set_tree("../example/customization")
t.run_build_system()
t.expect_addition(["bin/$toolset/debug*/codegen.exe",
"bin/$toolset/debug*/usage.cpp"])
t.cleanup()

View File

@@ -0,0 +1,30 @@
#!/usr/bin/python
# Copyright (C) Vladimir Prus 2006.
# Distributed under the Boost Software License, Version 1.0. (See
# accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
# Test the 'gettext' example.
import BoostBuild
import os
import string
t = BoostBuild.Tester()
t.set_tree("../example/gettext")
t.run_build_system(stderr=None)
t.expect_addition(["bin/$toolset/debug*/main.exe",
"bin/$toolset/debug*/russian.mo"])
file = t.adjust_names(["bin/$toolset/debug*/main.exe"])[0]
input_fd = os.popen(file)
input = input_fd.read();
t.fail_test(input.find("international hello") != 0)
t.cleanup()

View File

@@ -0,0 +1,21 @@
#!/usr/bin/python
# Copyright (C) Vladimir Prus 2006.
# Distributed under the Boost Software License, Version 1.0. (See
# accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
# Test the 'libraries' example.
import BoostBuild
t = BoostBuild.Tester(use_test_config=False)
t.set_tree("../example/libraries")
t.run_build_system()
t.expect_addition(["app/bin/$toolset/debug*/app.exe",
"util/foo/bin/$toolset/debug*/bar.dll"])
t.cleanup()

View File

@@ -0,0 +1,17 @@
#!/usr/bin/python
# Copyright (C) Vladimir Prus 2006.
# Distributed under the Boost Software License, Version 1.0. (See
# accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
# Test the 'make' example.
import BoostBuild
import sys
t = BoostBuild.Tester(['example.python.interpreter=%s' % sys.executable])
t.set_tree("../example/make")
t.run_build_system()
t.expect_addition(["bin/main.cpp"])
t.cleanup()

View File

@@ -0,0 +1,26 @@
#!/usr/bin/python
# Copyright (C) Vladimir Prus 2006.
# Distributed under the Boost Software License, Version 1.0. (See
# accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
# Test the 'qt4' examples.
import BoostBuild
t = BoostBuild.Tester()
t.set_tree("../example/qt/qt4/hello")
t.run_build_system()
t.expect_addition(["bin/$toolset/debug*/threading-multi/arrow"])
t.set_tree("../example/qt/qt4/moccable-cpp")
t.run_build_system()
t.expect_addition(["bin/$toolset/debug*/threading-multi/main"])
t.set_tree("../example/qt/qt4/uic")
t.run_build_system()
t.expect_addition(["bin/$toolset/debug*/threading-multi/hello"])
t.cleanup()

View File

@@ -0,0 +1,26 @@
#!/usr/bin/python
# Copyright (C) Vladimir Prus 2010.
# Distributed under the Boost Software License, Version 1.0. (See
# accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
# Test that build failure results in non-zero exit status
import BoostBuild
# Create a temporary working directory.
t = BoostBuild.Tester()
# Create the needed files.
t.write("jamroot.jam", """
exe hello : hello.cpp ;
""")
t.write("hello.cpp", """
int main() {
""")
t.run_build_system(status=1)
t.cleanup()

View File

@@ -0,0 +1,140 @@
#!/usr/bin/python
# Copyright 2003 Vladimir Prus
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
import BoostBuild
t = BoostBuild.Tester(arguments=["--config="], pass_toolset=0)
t.write("source.input", "")
t.write("test-properties.jam", """
import feature : feature ;
import generators ;
import toolset ;
import type ;
# We're not using the toolset at all, and we want to
# suppress toolset initialization to avoid surprises.
feature.extend toolset : null ;
type.register CHECK : check ;
type.register INPUT : input ;
feature expected-define : : free ;
feature unexpected-define : : free ;
toolset.flags test-properties DEFINES : <define> ;
toolset.flags test-properties EXPECTED : <expected-define> ;
toolset.flags test-properties UNEXPECTED : <unexpected-define> ;
generators.register-standard test-properties.check : INPUT : CHECK ;
rule check ( target : source : properties * )
{
local defines = [ on $(target) return $(DEFINES) ] ;
for local macro in [ on $(target) return $(EXPECTED) ]
{
if ! ( $(macro) in $(defines) )
{
EXIT expected $(macro) for $(target) in $(properties) : 1 ;
}
}
for local macro in [ on $(target) return $(UNEXPECTED) ]
{
if $(macro) in $(defines)
{
EXIT unexpected $(macro) for $(target) in $(properties) : 1 ;
}
}
}
actions check
{
echo okay > $(<)
}
""")
t.write("jamfile.jam", """
import test-properties ;
# See if default value of composite feature 'cf' will be expanded to
# <define>CF_IS_OFF.
check a : source.input : <expected-define>CF_IS_OFF ;
# See if subfeature in requirements in expanded.
check b : source.input : <cf>on-1
<expected-define>CF_1 <unexpected-define>CF_IS_OFF ;
# See if conditional requirements are recursively expanded.
check c : source.input : <toolset>null:<variant>release
<variant>release:<define>FOO <expected-define>FOO
;
# Composites specified in the default build should not
# be expanded if they are overridden in the the requirements.
check d : source.input : <cf>on <unexpected-define>CF_IS_OFF : <cf>off ;
# Overriding a feature should clear subfeatures and
# apply default values of subfeatures.
check e : source.input : <cf>always
<unexpected-define>CF_IS_OFF <expected-define>CF_2 <unexpected-define>CF_1
: <cf>on-1 ;
# Subfeatures should not be changed if the parent feature doesn't change
check f : source.input : <cf>on <expected-define>CF_1 : <cf>on-1 ;
# If a subfeature is not specific to the value of the parent feature,
# then changing the parent value should not clear the subfeature.
check g : source.input : <fopt>off <expected-define>FOPT_2 : <fopt>on-2 ;
# If the default value of a composite feature adds an optional
# feature which has a subfeature with a default, then that
# default should be added.
check h : source.input : <expected-define>CX_2 ;
# If the default value of a feature is used, then the
# default value of its subfeatures should also be used.
check i : source.input : <expected-define>SF_1 ;
# Subfeatures should be expanded when listed in a
# target reference.
check j-impl : source.input : <expected-define>CF_1 ;
explicit j-impl ;
alias j : j-impl/<cf>on-1 ;
""")
t.write("jamroot.jam", """
import feature ;
feature.feature cf : off on always : composite incidental ;
feature.compose <cf>off : <define>CF_IS_OFF ;
feature.subfeature cf on : version : 1 2 : composite optional incidental ;
feature.compose <cf-on:version>1 : <define>CF_1 ;
feature.subfeature cf always : version : 1 2 : composite incidental ;
feature.compose <cf-always:version>1 : <define>CF_2 ;
feature.feature fopt : on off : optional incidental ;
feature.subfeature fopt : version : 1 2 : composite incidental ;
feature.compose <fopt-version>2 : <define>FOPT_2 ;
feature.feature cx1 : on : composite incidental ;
feature.feature cx2 : on : optional incidental ;
feature.subfeature cx2 on : sub : 1 : composite incidental ;
feature.compose <cx1>on : <cx2>on ;
feature.compose <cx2-on:sub>1 : <define>CX_2 ;
feature.feature sf : a : incidental ;
feature.subfeature sf a : sub : 1 : composite incidental ;
feature.compose <sf-a:sub>1 : <define>SF_1 ;
""")
t.expand_toolset("jamfile.jam")
t.run_build_system()
t.expect_addition(["bin/debug/a.check",
"bin/debug/b.check",
"bin/null/release/c.check",
"bin/debug/d.check",
"bin/debug/e.check",
"bin/debug/f.check",
"bin/debug/g.check",
"bin/debug/h.check",
"bin/debug/i.check"])
t.cleanup()

View File

@@ -0,0 +1,59 @@
#!/usr/bin/python
# Copyright 2003 Vladimir Prus
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
import BoostBuild
t = BoostBuild.Tester(use_test_config=False)
t.write("jamroot.jam", """\
exe hello : hello.cpp ;
exe hello2 : hello.cpp ;
explicit hello2 ;
""")
t.write("hello.cpp", "int main() {}\n")
t.run_build_system()
t.ignore("*.tds")
t.expect_addition(BoostBuild.List("bin/$toolset/debug*/hello") * \
[".exe", ".obj"])
t.ignore_addition("bin/*/hello.rsp")
t.expect_nothing_more()
t.run_build_system(["hello2"])
t.expect_addition("bin/$toolset/debug*/hello2.exe")
t.rm(".")
# Test that 'explicit' used in a helper rule applies to the current project, and
# not to the Jamfile where the helper rule is defined.
t.write("jamroot.jam", """\
rule myinstall ( name : target )
{
install $(name)-bin : $(target) ;
explicit $(name)-bin ;
alias $(name) : $(name)-bin ;
}
""")
t.write("sub/a.cpp", "\n")
t.write("sub/jamfile.jam", "myinstall dist : a.cpp ;")
t.run_build_system(subdir="sub")
t.expect_addition("sub/dist-bin/a.cpp")
t.rm("sub/dist-bin")
t.write("sub/jamfile.jam", """\
myinstall dist : a.cpp ;
explicit dist ;
""")
t.run_build_system(subdir="sub")
t.expect_nothing_more()
t.cleanup()

View File

@@ -0,0 +1,37 @@
#!/usr/bin/python
# Copyright 2014 Steven Watanabe
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
# Tests the cxxflags feature
import BoostBuild
t = BoostBuild.Tester(use_test_config=False)
# cxxflags should be applied to C++ compilation,
# but not to C.
t.write("Jamroot.jam", """
obj test-cpp : test.cpp : <cxxflags>-DOKAY ;
obj test-c : test.c : <cxxflags>-DBAD ;
""")
t.write("test.cpp", """
#ifndef OKAY
#error Cannot compile without OKAY
#endif
""")
t.write("test.c", """
#ifdef BAD
#error Cannot compile with BAD
#endif
""")
t.run_build_system()
t.expect_addition("bin/$toolset/debug*/test-cpp.obj")
t.expect_addition("bin/$toolset/debug*/test-c.obj")
t.cleanup()

View File

@@ -0,0 +1,41 @@
#!/usr/bin/env python
# Copyright 2020 Nikita Kniazev
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
# Tests the force-include feature
import BoostBuild
t = BoostBuild.Tester(use_test_config=False)
t.write("Jamroot.jam", """
obj test-cpp : test.cpp : <force-include>one.h <force-include>two.h ;
obj test-c : test.c : <force-include>one.h <force-include>two.h ;
""")
for name in ("test.cpp", "test.c"):
t.write(name, """
#ifndef ONE
#error Cannot compile without ONE
#endif
#ifndef TWO
#error Cannot compile without TWO
#endif
""")
t.write("one.h", """
#define ONE
""")
t.write("two.h", """
#define TWO
""")
t.run_build_system()
t.expect_addition("bin/$toolset/debug*/test-cpp.obj")
t.expect_addition("bin/$toolset/debug*/test-c.obj")
t.cleanup()

View File

@@ -0,0 +1,113 @@
#!/usr/bin/python
# Copyright (c) Steven Watanabe 2018.
# Distributed under the Boost Software License, Version 1.0. (See
# accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
# Tests that a single main target can be used for
# implicit dependencies of multiple different types.
import BoostBuild
t = BoostBuild.Tester(pass_toolset=False)
t.write("input.sss", "")
t.write("Jamroot.jam", """
import type ;
import common ;
import generators ;
import "class" : new ;
import feature : feature ;
import toolset : flags ;
type.register AAA : aaa ;
type.register BBB : bbb ;
type.register CCC : ccc ;
type.register DDD : ddd ;
type.register SSS : sss ;
feature aaa-path : : free path ;
feature bbb-path : : free path ;
class aaa-action : action
{
rule adjust-properties ( property-set )
{
local s = [ $(self.targets[1]).creating-subvariant ] ;
return [ $(property-set).add-raw
[ $(s).implicit-includes aaa-path : AAA ] ] ;
}
}
class aaa-generator : generator
{
rule action-class ( )
{
return aaa-action ;
}
}
class bbb-action : action
{
rule adjust-properties ( property-set )
{
local s = [ $(self.targets[1]).creating-subvariant ] ;
return [ $(property-set).add-raw
[ $(s).implicit-includes bbb-path : BBB ] ] ;
}
}
class bbb-generator : generator
{
rule action-class ( )
{
return bbb-action ;
}
}
generators.register-standard common.copy : SSS : AAA ;
generators.register-standard common.copy : SSS : BBB ;
# Produce two targets from a single source
rule make-aaa-bbb ( project name ? : property-set : sources * )
{
local result ;
local aaa = [ generators.construct $(project) $(name) : AAA :
[ $(property-set).add-raw <location-prefix>a-loc ] : $(sources) ] ;
local bbb = [ generators.construct $(project) $(name) : BBB :
[ $(property-set).add-raw <location-prefix>b-loc ] : $(sources) ] ;
return [ $(aaa[1]).add $(bbb[1]) ] $(aaa[2-]) $(bbb[2-]) ;
}
generate input : input.sss : <generating-rule>@make-aaa-bbb ;
explicit input ;
flags make-ccc AAAPATH : <aaa-path> ;
rule make-ccc ( target : sources * : properties * )
{
ECHO aaa path\: [ on $(target) return $(AAAPATH) ] ;
common.copy $(target) : $(sources) ;
}
flags make-ddd BBBPATH : <bbb-path> ;
rule make-ddd ( target : sources * : properties * )
{
ECHO bbb path\: [ on $(target) return $(BBBPATH) ] ;
common.copy $(target) : $(sources) ;
}
generators.register [ new aaa-generator $(__name__).make-ccc : SSS : CCC ] ;
generators.register [ new bbb-generator $(__name__).make-ddd : SSS : DDD ] ;
# This should have <aaapath>bin/a-loc
ccc output-c : input.sss : <implicit-dependency>input ;
# This should have <bbbpath>bin/b-loc
ddd output-d : input.sss : <implicit-dependency>input ;
""")
t.run_build_system()
t.expect_output_lines(["aaa path: bin/a-loc", "bbb path: bin/b-loc"])
t.cleanup()

View File

@@ -0,0 +1,142 @@
#!/usr/bin/python
# Copyright 2018 Steven Watanabe
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
# Tests the <relevant> feature
import BoostBuild
t = BoostBuild.Tester(use_test_config=False)
t.write("xxx.jam", """
import type ;
import feature : feature ;
import toolset : flags ;
import generators ;
type.register XXX : xxx ;
type.register YYY : yyy ;
feature xxxflags : : free ;
generators.register-standard xxx.run : YYY : XXX ;
# xxxflags is relevant because it is used by flags
flags xxx.run OPTIONS : <xxxflags> ;
actions run
{
echo okay > $(<)
}
""")
t.write("zzz.jam", """
import xxx ;
import type ;
import feature : feature ;
import generators ;
type.register ZZZ : zzz ;
feature zzz.enabled : off on : propagated ;
# zzz.enabled is relevant because it is used in the generator's
# requirements
generators.register-standard zzz.run : XXX : ZZZ : <zzz.enabled>on ;
actions run
{
echo okay > $(<)
}
""")
t.write("aaa.jam", """
import zzz ;
import type ;
import feature : feature ;
import generators ;
import toolset : flags ;
type.register AAA : aaa ;
feature aaaflags : : free ;
generators.register-standard aaa.run : ZZZ : AAA ;
flags aaa.run OPTIONS : <aaaflags> ;
actions run
{
echo okay > $(<)
}
""")
t.write("Jamroot.jam", """
import xxx ;
import zzz ;
import aaa ;
import feature : feature ;
# f1 is relevant, because it is composite and <xxxflags> is relevant
feature f1 : n y : composite propagated ;
feature.compose <f1>y : <xxxflags>-no1 ;
# f2 is relevant, because it is used in a conditional
feature f2 : n y : propagated ;
# f3 is relevant, because it is used to choose the target alternative
feature f3 : n y : propagated ;
# f4 is relevant, because it is marked as such explicitly
feature f4 : n y : propagated ;
# f5 is relevant because of the conditional usage-requirements
feature f5 : n y : propagated ;
# f6 is relevant because the indirect conditional indicates so
feature f6 : n y : propagated ;
# f7 is relevant because the icond7 says so
feature f7 : n y : propagated ;
# The same as f[n], except not propagated
feature g1 : n y : composite ;
feature.compose <g1>y : <xxxflags>-no1 ;
feature g2 : n y ;
feature g3 : n y ;
feature g4 : n y ;
feature g5 : n y ;
feature g6 : n y ;
feature g7 : n y ;
project : default-build
<f1>y <f2>y <f3>y <f4>y <f5>y <f6>y <f7>y
<g1>y <g2>y <g3>y <g4>y <g5>y <g6>y <g7>y <zzz.enabled>on ;
rule icond6 ( properties * )
{
local result ;
if <f6>y in $(properties) || <g6>y in $(properties)
{
result += <xxxflags>-yes6 ;
}
return $(result)
<relevant>xxxflags:<relevant>f6
<relevant>xxxflags:<relevant>g6 ;
}
rule icond7 ( properties * )
{
local result ;
if <f7>y in $(properties) || <g7>y in $(properties)
{
result += <aaaflags>-yes7 ;
}
return $(result)
<relevant>aaaflags:<relevant>f7
<relevant>aaaflags:<relevant>g7 ;
}
zzz out : in.yyy
: <f2>y:<xxxflags>-no2 <g2>y:<xxxflags>-no2 <relevant>f4 <relevant>g4
<conditional>@icond6
:
: <f5>y:<aaaflags>-yes5 <g5>y:<aaaflags>-yes5 <conditional>@icond7
;
alias out : : <f3>n ;
alias out : : <g3>n ;
# Features that are relevant for out are also relevant for check-propagate
aaa check-propagate : out ;
""")
t.write("in.yyy", "")
t.run_build_system()
t.expect_addition("bin/f1-y/f2-y/f3-y/f4-y/f6-y/g1-y/g2-y/g3-y/g4-y/g6-y/out.xxx")
t.expect_addition("bin/f1-y/f2-y/f3-y/f4-y/f6-y/g1-y/g2-y/g3-y/g4-y/g6-y/zzz.enabled-on/out.zzz")
t.expect_addition("bin/f1-y/f2-y/f3-y/f4-y/f5-y/f6-y/f7-y/zzz.enabled-on/check-propagate.aaa")
t.cleanup()

View File

@@ -0,0 +1,33 @@
#!/usr/bin/python
# Copyright 2018 Steven Watanabe
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
# Tests the suppress-import-lib feature
# This used to cause the pdb and the import lib to get mixed up
# if there are any exports.
import BoostBuild
t = BoostBuild.Tester(use_test_config=False)
t.write("Jamroot.jam", """
lib l : l.cpp : <suppress-import-lib>true ;
""")
t.write("l.cpp", """
void
#ifdef _WIN32
__declspec(dllexport)
#endif
f() {}
""")
t.run_build_system()
t.expect_addition("bin/$toolset/debug*/l.obj")
t.expect_addition("bin/$toolset/debug*/l.dll")
t.cleanup()

View File

@@ -0,0 +1,44 @@
#!/usr/bin/python
#
# Copyright 2018 Steven Watanabe
#
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
# Tests the mapping of various suffixes
# In particular, .so[.version] needs to
# be mapped as a SHARED_LIB.
import BoostBuild
t = BoostBuild.Tester()
t.write("Jamroot.jam", """\
import type : type ;
ECHO [ type source.c ] ;
ECHO [ type source.cc ] ;
ECHO [ type source.cxx ] ;
ECHO [ type source.cpp ] ;
ECHO [ type source.o ] ;
ECHO [ type source.obj ] ;
ECHO [ type boost_system.lib ] ;
ECHO [ type boost_system.so ] ;
ECHO [ type boost_system.dll ] ;
EXIT [ type boost_system.so.1.66.0 ] : 0 ;
""")
t.run_build_system(stdout="""\
C
CPP
CPP
CPP
OBJ
OBJ
STATIC_LIB
SHARED_LIB
SHARED_LIB
SHARED_LIB
""")
t.cleanup()

View File

@@ -0,0 +1,74 @@
#!/usr/bin/python
# Copyright (C) Steven Watanabe 2018
# Distributed under the Boost Software License, Version 1.0. (See
# accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
# Tests the check-has-flag rule
import BoostBuild
t = BoostBuild.Tester(use_test_config=False)
# We need an object file before we can run the actual test.
t.write('input.cpp', 'void f() {}\n')
t.write('Jamroot.jam', 'obj input : input.cpp ;')
t.run_build_system()
linker_input = t.glob_file('bin/$toolset/debug*/input.obj')
# Check every possible result of pass or fail.
t.write('Jamroot.jam', '''
import flags ;
import modules ;
OBJECT_FILE = [ modules.peek : OBJECT_FILE ] ;
obj fail_cpp : test.cpp : [ check-has-flag <cxxflags>--illegal-flag-cpp
: <define>ERROR : <define>OK ] ;
obj pass_cpp : test.cpp : [ check-has-flag <cxxflags>-DMACRO_CPP
: <define>OK : <define>ERROR ] ;
obj fail_c : test.cpp : [ check-has-flag <cflags>--illegal-flag-c
: <define>ERROR : <define>OK ] ;
obj pass_c : test.cpp : [ check-has-flag <cflags>-DMACRO_C
: <define>OK : <define>ERROR ] ;
obj fail_link : test.cpp : [ check-has-flag <linkflags>--illegal-flag-link
: <define>ERROR : <define>OK ] ;
# The only thing that we can be certain the linker
# will accept is the name of an object file.
obj pass_link : test.cpp : [ check-has-flag <linkflags>$(OBJECT_FILE)
: <define>OK : <define>ERROR ] ;
''')
t.write('test.cpp', '''
#ifdef ERROR
#error ERROR defined
#endif
#ifndef OK
#error ERROR not defined
#endif
''')
# Don't check the status immediately, so that we have a chance
# to print config.log. Also, we need a minimum of d2 to make
# sure that we always see the commands and output.
t.run_build_system(['-sOBJECT_FILE=' + linker_input, '-d2'], status=None)
if t.status != 0:
log_file = t.read('bin/config.log')
BoostBuild.annotation("config.log", log_file)
t.fail_test(True)
t.expect_output_lines([' - has --illegal-flag-cpp : no*',
' - has -DMACRO_CPP : yes*',
' - has --illegal-flag-c : no*',
' - has -DMACRO_C : yes*',
' - has --illegal-flag-link : no*',
' - has *bin*/input.* : yes*'])
t.expect_addition('bin/$toolset/debug*/fail_cpp.obj')
t.expect_addition('bin/$toolset/debug*/pass_cpp.obj')
t.expect_addition('bin/$toolset/debug*/fail_c.obj')
t.expect_addition('bin/$toolset/debug*/pass_c.obj')
t.expect_addition('bin/$toolset/debug*/fail_link.obj')
t.expect_addition('bin/$toolset/debug*/pass_link.obj')
t.cleanup()

View File

@@ -0,0 +1,27 @@
#!/usr/bin/python
# Copyright 2004 Vladimir Prus
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at
# https://www.bfgroup.xyz/b2/LICENSE.txt)
# Tests that on gcc, we correctly report a problem when static runtime is
# requested for building a shared library.
import BoostBuild
t = BoostBuild.Tester()
t.write("jamroot.jam", "lib hello : hello.cpp ;")
t.write("hello.cpp", "int main() {}\n")
t.run_build_system(["runtime-link=static"])
t.expect_output_lines("warning: On gcc, DLLs can not be built with "
"'<runtime-link>static'.")
t.expect_nothing_more()
t.run_build_system(["link=static", "runtime-link=static"])
t.expect_addition("bin/$toolset/debug*/link-static*/hello.obj")
t.expect_addition("bin/$toolset/debug*/link-static*/hello.lib")
t.expect_nothing_more()
t.cleanup()

Some files were not shown because too many files have changed in this diff Show More