1#!/usr/local/bin/python3.8
2
3# Copyright 2008, 2012 Jurko Gospodnetic
4# Distributed under the Boost Software License, Version 1.0.
5# (See accompanying file LICENSE_1_0.txt or copy at
6# http://www.boost.org/LICENSE_1_0.txt)
7
8# Test Boost Build configuration file handling.
9
10import BoostBuild
11import TestCmd
12
13import os
14import os.path
15import re
16
17
18###############################################################################
19#
20# test_user_configuration()
21# -------------------------
22#
23###############################################################################
24
25def test_user_configuration():
26    """
27      Test Boost Build user configuration handling. Both relative and absolute
28    path handling is tested.
29
30    """
31
32    implicitConfigLoadMessage =  \
33        "notice: Loading user-config configuration file: *"
34    explicitConfigLoadMessage =  \
35        "notice: Loading explicitly specified user configuration file:"
36    disabledConfigLoadMessage =  \
37        "notice: User configuration file loading explicitly disabled."
38    testMessage = "_!_!_!_!_!_!_!_!_ %s _!_!_!_!_!_!_!_!_"
39    toolsetName = "__myDummyToolset__"
40    subdirName = "ASubDirectory"
41    configFileNames = ["ups_lala_1.jam", "ups_lala_2.jam",
42        os.path.join(subdirName, "ups_lala_3.jam")]
43
44    t = BoostBuild.Tester(["toolset=%s" % toolsetName,
45        "--debug-configuration"], pass_toolset=False, use_test_config=False)
46
47    for configFileName in configFileNames:
48        message = "ECHO \"%s\" ;" % testMessage % configFileName
49        # We need to double any backslashes in the message or Jam will
50        # interpret them as escape characters.
51        t.write(configFileName, message.replace("\\", "\\\\"))
52
53    # Prepare a dummy toolset so we do not get errors in case the default one
54    # is not found.
55    t.write(toolsetName + ".jam", """\
56import feature ;
57feature.extend toolset : %s ;
58rule init ( ) { }
59""" % toolsetName)
60
61    # Python version of the same dummy toolset.
62    t.write(toolsetName + ".py", """\
63from b2.build import feature
64feature.extend('toolset', ['%s'])
65def init(): pass
66""" % toolsetName)
67
68    t.write("jamroot.jam", """\
69local test-index = [ MATCH ---test-id---=(.*) : [ modules.peek : ARGV ] ] ;
70ECHO test-index: $(test-index:E=(unknown)) ;
71""")
72
73    class LocalTester:
74        def __init__(self, tester):
75            self.__tester = tester
76            self.__test_ids = []
77
78        def __assertionFailure(self, message):
79            BoostBuild.annotation("failure", "Internal test assertion failure "
80                "- %s" % message)
81            self.__tester.fail_test(1)
82
83        def __call__(self, test_id, env, extra_args=None, *args, **kwargs):
84            if env == "" and not canSetEmptyEnvironmentVariable:
85                self.__assertionFailure("Can not set empty environment "
86                    "variables on this platform.")
87            self.__registerTestId(str(test_id))
88            if extra_args is None:
89                extra_args = []
90            extra_args.append("---test-id---=%s" % test_id)
91            env_name = "BOOST_BUILD_USER_CONFIG"
92            previous_env = os.environ.get(env_name)
93            _env_set(env_name, env)
94            try:
95                self.__tester.run_build_system(extra_args, *args, **kwargs)
96            finally:
97                _env_set(env_name, previous_env)
98
99        def __registerTestId(self, test_id):
100            if test_id in self.__test_ids:
101                self.__assertionFailure("Multiple test cases encountered "
102                    "using the same test id '%s'." % test_id)
103            self.__test_ids.append(test_id)
104
105    test = LocalTester(t)
106
107    test(1, None)
108    t.expect_output_lines(explicitConfigLoadMessage, False)
109    t.expect_output_lines(disabledConfigLoadMessage, False)
110    t.expect_output_lines(testMessage % configFileNames[0], False)
111    t.expect_output_lines(testMessage % configFileNames[1], False)
112    t.expect_output_lines(testMessage % configFileNames[2], False)
113
114    test(2, None, ["--user-config="])
115    t.expect_output_lines(implicitConfigLoadMessage, False)
116    t.expect_output_lines(explicitConfigLoadMessage, False)
117    t.expect_output_lines(disabledConfigLoadMessage)
118    t.expect_output_lines(testMessage % configFileNames[0], False)
119    t.expect_output_lines(testMessage % configFileNames[1], False)
120    t.expect_output_lines(testMessage % configFileNames[2], False)
121
122    test(3, None, ['--user-config=""'])
123    t.expect_output_lines(implicitConfigLoadMessage, False)
124    t.expect_output_lines(explicitConfigLoadMessage, False)
125    t.expect_output_lines(disabledConfigLoadMessage)
126    t.expect_output_lines(testMessage % configFileNames[0], False)
127    t.expect_output_lines(testMessage % configFileNames[1], False)
128    t.expect_output_lines(testMessage % configFileNames[2], False)
129
130    test(4, None, ['--user-config="%s"' % configFileNames[0]])
131    t.expect_output_lines(implicitConfigLoadMessage, False)
132    t.expect_output_lines(explicitConfigLoadMessage)
133    t.expect_output_lines(disabledConfigLoadMessage, False)
134    t.expect_output_lines(testMessage % configFileNames[0])
135    t.expect_output_lines(testMessage % configFileNames[1], False)
136    t.expect_output_lines(testMessage % configFileNames[2], False)
137
138    test(5, None, ['--user-config="%s"' % configFileNames[2]])
139    t.expect_output_lines(implicitConfigLoadMessage, False)
140    t.expect_output_lines(explicitConfigLoadMessage)
141    t.expect_output_lines(disabledConfigLoadMessage, False)
142    t.expect_output_lines(testMessage % configFileNames[0], False)
143    t.expect_output_lines(testMessage % configFileNames[1], False)
144    t.expect_output_lines(testMessage % configFileNames[2])
145
146    test(6, None, ['--user-config="%s"' % os.path.abspath(configFileNames[1])])
147    t.expect_output_lines(implicitConfigLoadMessage, False)
148    t.expect_output_lines(explicitConfigLoadMessage)
149    t.expect_output_lines(disabledConfigLoadMessage, False)
150    t.expect_output_lines(testMessage % configFileNames[0], False)
151    t.expect_output_lines(testMessage % configFileNames[1])
152    t.expect_output_lines(testMessage % configFileNames[2], False)
153
154    test(7, None, ['--user-config="%s"' % os.path.abspath(configFileNames[2])])
155    t.expect_output_lines(implicitConfigLoadMessage, False)
156    t.expect_output_lines(explicitConfigLoadMessage)
157    t.expect_output_lines(disabledConfigLoadMessage, False)
158    t.expect_output_lines(testMessage % configFileNames[0], False)
159    t.expect_output_lines(testMessage % configFileNames[1], False)
160    t.expect_output_lines(testMessage % configFileNames[2])
161
162    if canSetEmptyEnvironmentVariable:
163        test(8, "")
164        t.expect_output_lines(implicitConfigLoadMessage, False)
165        t.expect_output_lines(explicitConfigLoadMessage, False)
166        t.expect_output_lines(disabledConfigLoadMessage, True)
167        t.expect_output_lines(testMessage % configFileNames[0], False)
168        t.expect_output_lines(testMessage % configFileNames[1], False)
169        t.expect_output_lines(testMessage % configFileNames[2], False)
170
171    test(9, '""')
172    t.expect_output_lines(implicitConfigLoadMessage, False)
173    t.expect_output_lines(explicitConfigLoadMessage, False)
174    t.expect_output_lines(disabledConfigLoadMessage)
175    t.expect_output_lines(testMessage % configFileNames[0], False)
176    t.expect_output_lines(testMessage % configFileNames[1], False)
177    t.expect_output_lines(testMessage % configFileNames[2], False)
178
179    test(10, configFileNames[1])
180    t.expect_output_lines(implicitConfigLoadMessage, False)
181    t.expect_output_lines(explicitConfigLoadMessage)
182    t.expect_output_lines(disabledConfigLoadMessage, False)
183    t.expect_output_lines(testMessage % configFileNames[0], False)
184    t.expect_output_lines(testMessage % configFileNames[1])
185    t.expect_output_lines(testMessage % configFileNames[2], False)
186
187    test(11, configFileNames[1], ['--user-config=""'])
188    t.expect_output_lines(implicitConfigLoadMessage, False)
189    t.expect_output_lines(explicitConfigLoadMessage, False)
190    t.expect_output_lines(disabledConfigLoadMessage)
191    t.expect_output_lines(testMessage % configFileNames[0], False)
192    t.expect_output_lines(testMessage % configFileNames[1], False)
193    t.expect_output_lines(testMessage % configFileNames[2], False)
194
195    test(12, configFileNames[1], ['--user-config="%s"' % configFileNames[0]])
196    t.expect_output_lines(implicitConfigLoadMessage, False)
197    t.expect_output_lines(explicitConfigLoadMessage)
198    t.expect_output_lines(disabledConfigLoadMessage, False)
199    t.expect_output_lines(testMessage % configFileNames[0])
200    t.expect_output_lines(testMessage % configFileNames[1], False)
201    t.expect_output_lines(testMessage % configFileNames[2], False)
202
203    if canSetEmptyEnvironmentVariable:
204        test(13, "", ['--user-config="%s"' % configFileNames[0]])
205        t.expect_output_lines(implicitConfigLoadMessage, False)
206        t.expect_output_lines(explicitConfigLoadMessage)
207        t.expect_output_lines(disabledConfigLoadMessage, False)
208        t.expect_output_lines(testMessage % configFileNames[0])
209        t.expect_output_lines(testMessage % configFileNames[1], False)
210        t.expect_output_lines(testMessage % configFileNames[2], False)
211
212    test(14, '""', ['--user-config="%s"' % configFileNames[0]])
213    t.expect_output_lines(implicitConfigLoadMessage, False)
214    t.expect_output_lines(explicitConfigLoadMessage)
215    t.expect_output_lines(disabledConfigLoadMessage, False)
216    t.expect_output_lines(testMessage % configFileNames[0])
217    t.expect_output_lines(testMessage % configFileNames[1], False)
218    t.expect_output_lines(testMessage % configFileNames[2], False)
219
220    test(15, "invalid", ['--user-config="%s"' % configFileNames[0]])
221    t.expect_output_lines(implicitConfigLoadMessage, False)
222    t.expect_output_lines(explicitConfigLoadMessage)
223    t.expect_output_lines(disabledConfigLoadMessage, False)
224    t.expect_output_lines(testMessage % configFileNames[0])
225    t.expect_output_lines(testMessage % configFileNames[1], False)
226    t.expect_output_lines(testMessage % configFileNames[2], False)
227
228    t.cleanup()
229
230
231###############################################################################
232#
233# Private interface.
234#
235###############################################################################
236
237def _canSetEmptyEnvironmentVariable():
238    """
239      Unfortunately different OSs (and possibly Python implementations as well)
240    have different interpretations of what it means to set an environment
241    variable to an empty string. Some (e.g. Windows) interpret it as unsetting
242    the variable and some (e.g. AIX or Darwin) actually set it to an empty
243    string.
244
245    """
246    dummyName = "UGNABUNGA_FOO_BAR_BAZ_FEE_FAE_FOU_FAM"
247    original = os.environ.get(dummyName)
248    _env_set(dummyName, "")
249    result = _getExternalEnv(dummyName) == ""
250    _env_set(dummyName, original)
251    return result
252
253
254def _env_del(name):
255    """
256      Unsets the given environment variable if it is currently set.
257
258      Note that we can not use os.environ.pop() or os.environ.clear() here
259    since prior to Python 2.6 these functions did not remove the actual
260    environment variable by calling os.unsetenv().
261
262    """
263    try:
264        del os.environ[name]
265    except KeyError:
266        pass
267
268
269def _env_set(name, value):
270    """
271      Sets the given environment variable value or unsets it, if the value is
272    None.
273
274    """
275    if value is None:
276        _env_del(name)
277    else:
278        os.environ[name] = value
279
280
281def _getExternalEnv(name):
282    toolsetName = "__myDummyToolset__"
283
284    t = BoostBuild.Tester(["toolset=%s" % toolsetName], pass_toolset=False,
285        use_test_config=False)
286    try:
287        #   Prepare a dummy toolset so we do not get errors in case the default
288        # one is not found.
289        t.write(toolsetName + ".jam", """\
290import feature ;
291feature.extend toolset : %s ;
292rule init ( ) { }
293""" % toolsetName)
294
295        # Python version of the same dummy toolset.
296        t.write(toolsetName + ".py", """\
297from b2.build import feature
298feature.extend('toolset', ['%s'])
299def init(): pass
300""" % toolsetName)
301
302        t.write("jamroot.jam", """\
303import os ;
304local names = [ MATCH ^---var-name---=(.*) : [ modules.peek : ARGV ] ] ;
305for x in $(names)
306{
307    value = [ os.environ $(x) ] ;
308    ECHO "###" $(x): '$(value)' "###" ;
309}
310""")
311
312        t.run_build_system(["---var-name---=%s" % name])
313        m = re.search("^### %s: '(.*)' ###$" % name, t.stdout(), re.MULTILINE)
314        if m:
315            return m.group(1)
316    finally:
317        t.cleanup()
318
319
320def test_site_config():
321    # Ignore user-config, just in case it depends on the user's site-config.jam
322    t = BoostBuild.Tester(["--user-config="], use_test_config=False,
323                          pass_toolset=0)
324    # We can immediately exit after we finish loading the config files
325    t.write("Jamroot", "EXIT Done : 0 ;")
326    t.write("my-site-config.jam", "ECHO Loaded my-site-config ;")
327
328    t.run_build_system(["--site-config=my-site-config.jam"],
329                       stdout="Loaded my-site-config\nDone\n")
330
331    t.run_build_system(["--ignore-site-config", "--debug-configuration"])
332    t.expect_output_lines("""\
333notice: Site configuration files will be ignored due to the
334notice: --ignore-site-config command-line option.""")
335
336    t.run_build_system(["--site-config=", "--debug-configuration"])
337    t.expect_output_lines("""\
338notice: Site configuration file loading explicitly disabled.""")
339
340    t.cleanup()
341
342def test_global_config():
343    t = BoostBuild.Tester(use_test_config=False, pass_toolset=0)
344    t.write("my-config.jam", "ECHO Loading my-config ;")
345    t.write("Jamroot", "EXIT Done : 0 ;")
346    t.write("project-config.jam", "ECHO bad ;")
347    t.run_build_system(["--config=my-config.jam", "--debug-configuration"],
348                       match=TestCmd.match_re, stdout=
349r"""notice: found boost-build\.jam at .*
350notice: loading B2 from .*
351notice: Searching '.*' for all-config configuration file 'my-config\.jam'\.
352notice: Loading all-config configuration file 'my-config\.jam' from '.*'\.
353Loading my-config
354notice: Regular configuration files will be ignored due
355notice: to the global configuration being loaded\.
356Done
357""")
358    t.run_build_system(["--config=", "--debug-configuration"],
359                       match=TestCmd.match_re, stdout=
360r"""notice: found boost-build\.jam at .*
361notice: loading B2 from .*
362notice: Configuration file loading explicitly disabled.
363Done
364""")
365    t.cleanup()
366
367def test_project_config():
368    t = BoostBuild.Tester(["--user-config=", "--site-config="],
369                          use_test_config=False, pass_toolset=False)
370    t.write("Jamroot", "EXIT Done : 0 ;")
371    t.write("project-config.jam", "ECHO Loading Root ;")
372    t.write("my-project-config.jam", "ECHO Loading explicit ;")
373    t.write("sub/project-config.jam", "ECHO Loading subdir ;")
374    t.write("sub/Jamfile", "")
375
376    t.run_build_system(stdout="Loading Root\nDone\n")
377    t.run_build_system(subdir="sub", stdout="Loading subdir\nDone\n")
378    t.rm("sub/project-config.jam")
379    t.run_build_system(subdir="sub", stdout="Loading Root\nDone\n")
380    t.run_build_system(["--project-config=my-project-config.jam"],
381                       stdout="Loading explicit\nDone\n")
382
383    t.cleanup()
384
385###############################################################################
386#
387# main()
388# ------
389#
390###############################################################################
391
392canSetEmptyEnvironmentVariable = _canSetEmptyEnvironmentVariable()
393
394test_user_configuration()
395test_site_config()
396test_global_config()
397test_project_config()
398