mirror of
				https://github.com/saitohirga/WSJT-X.git
				synced 2025-10-30 20:40:28 -04:00 
			
		
		
		
	
		
			
	
	
		
			434 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			434 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
|  | #!/usr/bin/python | ||
|  | 
 | ||
|  | # Copyright 2002, 2003 Dave Abrahams | ||
|  | # Copyright 2002, 2003, 2004, 2005 Vladimir Prus | ||
|  | # Copyright 2012 Jurko Gospodnetic | ||
|  | # Distributed under the Boost Software License, Version 1.0. | ||
|  | # (See accompanying file LICENSE_1_0.txt or copy at | ||
|  | # http://www.boost.org/LICENSE_1_0.txt) | ||
|  | 
 | ||
|  | import BoostBuild | ||
|  | import re | ||
|  | 
 | ||
|  | 
 | ||
|  | def test_basic(): | ||
|  |     t = BoostBuild.Tester(pass_d0=False) | ||
|  |     __write_appender(t, "appender.jam") | ||
|  |     t.write("a.cpp", "") | ||
|  |     t.write("b.cxx", "") | ||
|  |     t.write("c.tui", "") | ||
|  |     t.write("d.wd", "") | ||
|  |     t.write("e.cpp", "") | ||
|  |     t.write("x.l", "") | ||
|  |     t.write("y.x_pro", "") | ||
|  |     t.write("z.cpp", "") | ||
|  |     t.write("lib/c.cpp", "int bar() { return 0; }\n") | ||
|  |     t.write("lib/jamfile.jam", "my-lib auxilliary : c.cpp ;") | ||
|  |     t.write("jamroot.jam", | ||
|  | r"""import appender ;
 | ||
|  | 
 | ||
|  | import "class" : new ; | ||
|  | import generators ; | ||
|  | import type ; | ||
|  | 
 | ||
|  | 
 | ||
|  | ################################################################################ | ||
|  | # | ||
|  | #   We use our own custom EXE, LIB & OBJ target generators as using the regular | ||
|  | # ones would force us to have to deal with different compiler/linker specific | ||
|  | # 'features' that really have nothing to do with this test. For example, IBM XL | ||
|  | # C/C++ for AIX, V12.1 (Version: 12.01.0000.0000) compiler exits with a non-zero | ||
|  | # exit code and thus fails our build when run with a source file using an | ||
|  | # unknown suffix like '.marked_cpp'. | ||
|  | # | ||
|  | ################################################################################ | ||
|  | 
 | ||
|  | type.register MY_EXE : my_exe ; | ||
|  | type.register MY_LIB : my_lib ; | ||
|  | type.register MY_OBJ : my_obj ; | ||
|  | 
 | ||
|  | appender.register compile-c : C : MY_OBJ ; | ||
|  | appender.register compile-cpp : CPP : MY_OBJ ; | ||
|  | appender.register link-lib composing : MY_OBJ : MY_LIB ; | ||
|  | appender.register link-exe composing : MY_OBJ MY_LIB : MY_EXE ; | ||
|  | 
 | ||
|  | 
 | ||
|  | ################################################################################ | ||
|  | # | ||
|  | # LEX --> C | ||
|  | # | ||
|  | ################################################################################ | ||
|  | 
 | ||
|  | type.register LEX : l ; | ||
|  | 
 | ||
|  | appender.register lex-to-c : LEX : C ; | ||
|  | 
 | ||
|  | 
 | ||
|  | ################################################################################ | ||
|  | # | ||
|  | #        /--> tUI_H --\ | ||
|  | # tUI --<              >--> CPP | ||
|  | #        \------------/ | ||
|  | # | ||
|  | ################################################################################ | ||
|  | 
 | ||
|  | type.register tUI : tui ; | ||
|  | type.register tUI_H : tui_h ; | ||
|  | 
 | ||
|  | appender.register ui-to-cpp : tUI tUI_H : CPP ; | ||
|  | appender.register ui-to-h : tUI : tUI_H ; | ||
|  | 
 | ||
|  | 
 | ||
|  | ################################################################################ | ||
|  | # | ||
|  | #          /--> X1 --\ | ||
|  | # X_PRO --<           >--> CPP | ||
|  | #          \--> X2 --/ | ||
|  | # | ||
|  | ################################################################################ | ||
|  | 
 | ||
|  | type.register X1 : x1 ; | ||
|  | type.register X2 : x2 ; | ||
|  | type.register X_PRO : x_pro ; | ||
|  | 
 | ||
|  | appender.register x1-x2-to-cpp : X1 X2 : CPP ; | ||
|  | appender.register x-pro-to-x1-x2 : X_PRO : X1 X2 ; | ||
|  | 
 | ||
|  | 
 | ||
|  | ################################################################################ | ||
|  | # | ||
|  | #   When the main target type is NM_EXE, build OBJ from CPP-MARKED and not from | ||
|  | # anything else, e.g. directly from CPP. | ||
|  | # | ||
|  | ################################################################################ | ||
|  | 
 | ||
|  | type.register CPP_MARKED : marked_cpp : CPP ; | ||
|  | type.register POSITIONS : positions ; | ||
|  | type.register NM.TARGET.CPP : target_cpp : CPP ; | ||
|  | type.register NM_EXE : : MY_EXE ; | ||
|  | 
 | ||
|  | appender.register marked-to-target-cpp : CPP_MARKED : NM.TARGET.CPP ; | ||
|  | appender.register cpp-to-marked-positions : CPP : CPP_MARKED POSITIONS ; | ||
|  | 
 | ||
|  | class nm::target::cpp-obj-generator : generator | ||
|  | { | ||
|  |     rule __init__ ( id ) | ||
|  |     { | ||
|  |         generator.__init__ $(id) : NM.TARGET.CPP : MY_OBJ ; | ||
|  |         generator.set-rule-name appender.appender ; | ||
|  |     } | ||
|  | 
 | ||
|  |     rule requirements ( ) | ||
|  |     { | ||
|  |         return <main-target-type>NM_EXE ; | ||
|  |     } | ||
|  | 
 | ||
|  |     rule run ( project name ? : properties * : source : multiple ? ) | ||
|  |     { | ||
|  |         if [ $(source).type ] = CPP | ||
|  |         { | ||
|  |             local converted = [ generators.construct $(project) : NM.TARGET.CPP | ||
|  |                 : $(properties) : $(source) ] ; | ||
|  |             if $(converted) | ||
|  |             { | ||
|  |                 return [ generators.construct $(project) : MY_OBJ : | ||
|  |                     $(properties) : $(converted[2]) ] ; | ||
|  |             } | ||
|  |         } | ||
|  |     } | ||
|  | } | ||
|  | generators.register [ new nm::target::cpp-obj-generator target-obj ] ; | ||
|  | generators.override target-obj : all ; | ||
|  | 
 | ||
|  | 
 | ||
|  | ################################################################################ | ||
|  | # | ||
|  | # A more complex test case scenario with the following generators: | ||
|  | #  1. WHL --> CPP, WHL_LR0, H, H(%_symbols) | ||
|  | #  2. DLP --> CPP | ||
|  | #  3. WD --> WHL(%_parser) DLP(%_lexer) | ||
|  | #  4. A custom generator of higher priority than generators 1. & 2. that helps | ||
|  | #     disambiguate between them when generating CPP files from WHL and DLP | ||
|  | #     sources. | ||
|  | # | ||
|  | ################################################################################ | ||
|  | 
 | ||
|  | type.register WHL : whl ; | ||
|  | type.register DLP : dlp ; | ||
|  | type.register WHL_LR0 : lr0 ; | ||
|  | type.register WD : wd ; | ||
|  | 
 | ||
|  | local whale-generator-id = [ appender.register whale : WHL : CPP WHL_LR0 H | ||
|  |     H(%_symbols) ] ; | ||
|  | local dolphin-generator-id = [ appender.register dolphin : DLP : CPP ] ; | ||
|  | appender.register wd : WD : WHL(%_parser) DLP(%_lexer) ; | ||
|  | 
 | ||
|  | class wd-to-cpp : generator | ||
|  | { | ||
|  |     rule __init__ ( id : sources * : targets * ) | ||
|  |     { | ||
|  |         generator.__init__ $(id) : $(sources) : $(targets) ; | ||
|  |     } | ||
|  | 
 | ||
|  |     rule run ( project name ? : property-set : source ) | ||
|  |     { | ||
|  |         local new-sources = $(source) ; | ||
|  |         if ! [ $(source).type ] in WHL DLP | ||
|  |         { | ||
|  |             local r1 = [ generators.construct $(project) $(name) : WHL : | ||
|  |                 $(property-set) : $(source) ] ; | ||
|  |             local r2 = [ generators.construct $(project) $(name) : DLP : | ||
|  |                 $(property-set) : $(source) ] ; | ||
|  |             new-sources = [ sequence.unique $(r1[2-]) $(r2[2-]) ] ; | ||
|  |         } | ||
|  | 
 | ||
|  |         local result ; | ||
|  |         for local i in $(new-sources) | ||
|  |         { | ||
|  |             local t = [ generators.construct $(project) $(name) : CPP : | ||
|  |                 $(property-set) : $(i) ] ; | ||
|  |             result += $(t[2-]) ; | ||
|  |         } | ||
|  |         return $(result) ; | ||
|  |     } | ||
|  | } | ||
|  | generators.override $(__name__).wd-to-cpp : $(whale-generator-id) ; | ||
|  | generators.override $(__name__).wd-to-cpp : $(dolphin-generator-id) ; | ||
|  | generators.register [ new wd-to-cpp $(__name__).wd-to-cpp : : CPP ] ; | ||
|  | 
 | ||
|  | 
 | ||
|  | ################################################################################ | ||
|  | # | ||
|  | # Declare build targets. | ||
|  | # | ||
|  | ################################################################################ | ||
|  | 
 | ||
|  | # This should not cause two CPP --> MY_OBJ constructions for a.cpp or b.cpp. | ||
|  | my-exe a : a.cpp b.cxx obj_1 obj_2 c.tui d.wd x.l y.x_pro lib//auxilliary ; | ||
|  | my-exe f : a.cpp b.cxx obj_1 obj_2 lib//auxilliary ; | ||
|  | 
 | ||
|  | # This should cause two CPP --> MY_OBJ constructions for z.cpp. | ||
|  | my-obj obj_1 : z.cpp ; | ||
|  | my-obj obj_2 : z.cpp ; | ||
|  | 
 | ||
|  | nm-exe e : e.cpp ; | ||
|  | """)
 | ||
|  | 
 | ||
|  |     t.run_build_system() | ||
|  |     t.expect_addition("bin/$toolset/debug/" * BoostBuild.List("a.my_exe " | ||
|  |         "a.my_obj b.my_obj c.tui_h c.cpp c.my_obj d_parser.whl d_lexer.dlp " | ||
|  |         "d_parser.cpp d_lexer.cpp d_lexer.my_obj d_parser.lr0 d_parser.h " | ||
|  |         "d_parser.my_obj d_parser_symbols.h x.c x.my_obj y.x1 y.x2 y.cpp " | ||
|  |         "y.my_obj e.marked_cpp e.positions e.target_cpp e.my_obj e.my_exe " | ||
|  |         "f.my_exe obj_1.my_obj obj_2.my_obj")) | ||
|  |     t.expect_addition("lib/bin/$toolset/debug/" * BoostBuild.List("c.my_obj " | ||
|  |         "auxilliary.my_lib")) | ||
|  |     t.expect_nothing_more() | ||
|  | 
 | ||
|  |     folder = "bin/$toolset/debug" | ||
|  |     t.expect_content_lines("%s/obj_1.my_obj" % folder, "     Sources: 'z.cpp'") | ||
|  |     t.expect_content_lines("%s/obj_2.my_obj" % folder, "     Sources: 'z.cpp'") | ||
|  |     t.expect_content_lines("%s/a.my_obj" % folder, "     Sources: 'a.cpp'") | ||
|  | 
 | ||
|  |     lines = t.stdout().splitlines() | ||
|  |     source_lines = [x for x in lines if re.match("^     Sources: '", x)] | ||
|  |     if not __match_count_is(source_lines, "'z.cpp'", 2): | ||
|  |         BoostBuild.annotation("failure", "z.cpp must be compiled exactly " | ||
|  |             "twice.") | ||
|  |         t.fail_test(1) | ||
|  |     if not __match_count_is(source_lines, "'a.cpp'", 1): | ||
|  |         BoostBuild.annotation("failure", "a.cpp must be compiled exactly " | ||
|  |             "once.") | ||
|  |         t.fail_test(1) | ||
|  |     t.cleanup() | ||
|  | 
 | ||
|  | 
 | ||
|  | def test_generated_target_names(): | ||
|  |     """
 | ||
|  |       Test generator generated target names. Unless given explicitly, target | ||
|  |     names should be determined based on their specified source names. All | ||
|  |     sources for generating a target need to have matching names in order for | ||
|  |     Boost Build to be able to implicitly determine the target's name. | ||
|  | 
 | ||
|  |       We use the following target generation structure with differently named | ||
|  |     BBX targets: | ||
|  |                        /---> BB1 ---\ | ||
|  |                 AAA --<----> BB2 ---->--> CCC --(composing)--> DDD | ||
|  |                        \---> BB3 ---/ | ||
|  | 
 | ||
|  |       The extra generator at the end is needed because generating a top-level | ||
|  |     CCC target directly would requires us to explicitly specify a name for it. | ||
|  |     The extra generator needs to be composing in order not to explicitly | ||
|  |     request a specific name for its CCC source target based on its own target | ||
|  |     name. | ||
|  | 
 | ||
|  |       We also check for a regression where only the first two sources were | ||
|  |     checked to see if their names match. Note that we need to try out all file | ||
|  |     renaming combinations as we do not know what ordering Boost Build is going | ||
|  |     to use when passing in those files as generator sources. | ||
|  | 
 | ||
|  |     """
 | ||
|  |     jamfile_template = """\
 | ||
|  | import type ; | ||
|  | type.register AAA : _a ; | ||
|  | type.register BB1 : _b1 ; | ||
|  | type.register BB2 : _b2 ; | ||
|  | type.register BB3 : _b3 ; | ||
|  | type.register CCC : _c ; | ||
|  | type.register DDD : _d ; | ||
|  | 
 | ||
|  | import appender ; | ||
|  | appender.register aaa-to-bbX           : AAA         : BB1%s BB2%s BB3%s ; | ||
|  | appender.register bbX-to-ccc           : BB1 BB2 BB3 : CCC ; | ||
|  | appender.register ccc-to-ddd composing : CCC         : DDD ; | ||
|  | 
 | ||
|  | ddd _xxx : _xxx._a ; | ||
|  | """
 | ||
|  | 
 | ||
|  |     t = BoostBuild.Tester(pass_d0=False) | ||
|  |     __write_appender(t, "appender.jam") | ||
|  |     t.write("_xxx._a", "") | ||
|  | 
 | ||
|  |     def test_one(t, rename1, rename2, rename3, status): | ||
|  |         def f(rename): | ||
|  |             if rename: return "(%_x)" | ||
|  |             return "" | ||
|  | 
 | ||
|  |         jamfile = jamfile_template % (f(rename1), f(rename2), f(rename3)) | ||
|  |         t.write("jamroot.jam", jamfile, wait=False) | ||
|  | 
 | ||
|  |         #   Remove any preexisting targets left over from a previous test run | ||
|  |         # so we do not have to be careful about tracking which files have been | ||
|  |         # newly added and which preexisting ones have only been modified. | ||
|  |         t.rm("bin") | ||
|  | 
 | ||
|  |         t.run_build_system(status=status) | ||
|  | 
 | ||
|  |         if status: | ||
|  |             t.expect_output_lines("*.bbX-to-ccc: source targets have " | ||
|  |                 "different names: cannot determine target name") | ||
|  |         else: | ||
|  |             def suffix(rename): | ||
|  |                 if rename: return "_x" | ||
|  |                 return "" | ||
|  |             name = "bin/$toolset/debug/_xxx" | ||
|  |             e = t.expect_addition | ||
|  |             e("%s%s._b1" % (name, suffix(rename1))) | ||
|  |             e("%s%s._b2" % (name, suffix(rename2))) | ||
|  |             e("%s%s._b3" % (name, suffix(rename3))) | ||
|  |             e("%s%s._c" % (name, suffix(rename1 and rename2 and rename3))) | ||
|  |             e("%s._d" % name) | ||
|  |         t.expect_nothing_more() | ||
|  | 
 | ||
|  |     test_one(t, False, False, False, status=0) | ||
|  |     test_one(t, True , False, False, status=1) | ||
|  |     test_one(t, False, True , False, status=1) | ||
|  |     test_one(t, False, False, True , status=1) | ||
|  |     test_one(t, True , True , False, status=1) | ||
|  |     test_one(t, True , False, True , status=1) | ||
|  |     test_one(t, False, True , True , status=1) | ||
|  |     test_one(t, True , True , True , status=0) | ||
|  |     t.cleanup() | ||
|  | 
 | ||
|  | 
 | ||
|  | def __match_count_is(lines, pattern, expected): | ||
|  |     count = 0 | ||
|  |     for x in lines: | ||
|  |         if re.search(pattern, x): | ||
|  |             count += 1 | ||
|  |         if count > expected: | ||
|  |             return False | ||
|  |     return count == expected | ||
|  | 
 | ||
|  | 
 | ||
|  | def __write_appender(t, name): | ||
|  |     t.write(name, | ||
|  | r"""# Copyright 2012 Jurko Gospodnetic
 | ||
|  | # Distributed under the Boost Software License, Version 1.0. | ||
|  | # (See accompanying file LICENSE_1_0.txt or copy at | ||
|  | # http://www.boost.org/LICENSE_1_0.txt) | ||
|  | 
 | ||
|  | #   Support for registering test generators that construct their targets by | ||
|  | # simply appending their given input data, e.g. list of sources & targets. | ||
|  | 
 | ||
|  | import "class" : new ; | ||
|  | import generators ; | ||
|  | import modules ; | ||
|  | import sequence ; | ||
|  | 
 | ||
|  | rule register ( id composing ? : source-types + : target-types + ) | ||
|  | { | ||
|  |     local caller-module = [ CALLER_MODULE ] ; | ||
|  |     id = $(caller-module).$(id) ; | ||
|  |     local g = [ new generator $(id) $(composing) : $(source-types) : | ||
|  |         $(target-types) ] ; | ||
|  |     $(g).set-rule-name $(__name__).appender ; | ||
|  |     generators.register $(g) ; | ||
|  |     return $(id) ; | ||
|  | } | ||
|  | 
 | ||
|  | if [ modules.peek : NT ] | ||
|  | { | ||
|  |     X = ")" ; | ||
|  |     ECHO_CMD = (echo. ; | ||
|  | } | ||
|  | else | ||
|  | { | ||
|  |     X = \" ; | ||
|  |     ECHO_CMD = "echo $(X)" ; | ||
|  | } | ||
|  | 
 | ||
|  | local appender-runs ; | ||
|  | 
 | ||
|  | # We set up separate actions for building each target in order to avoid having | ||
|  | # to iterate over them in action (i.e. shell) code. We have to be extra careful | ||
|  | # though to achieve the exact same effect as if doing all the work in just one | ||
|  | # action. Otherwise Boost Jam might, under some circumstances, run only some of | ||
|  | # our actions. To achieve this we register a series of actions for all the | ||
|  | # targets (since they all have the same target list - either all or none of them | ||
|  | # get run independent of which target actually needs to get built), each | ||
|  | # building only a single target. Since all our actions use the same targets, we | ||
|  | # can not use 'on-target' parameters to pass data to a specific action so we | ||
|  | # pass them using the second 'sources' parameter which our actions then know how | ||
|  | # to interpret correctly. This works well since Boost Jam does not automatically | ||
|  | # add dependency relations between specified action targets & sources and so the | ||
|  | # second argument, even though most often used to pass in a list of sources, can | ||
|  | # actually be used for passing in any type of information. | ||
|  | rule appender ( targets + : sources + : properties * ) | ||
|  | { | ||
|  |     appender-runs = [ CALC $(appender-runs:E=0) + 1 ] ; | ||
|  |     local target-index = 0 ; | ||
|  |     local target-count = [ sequence.length $(targets) ] ; | ||
|  |     local original-targets ; | ||
|  |     for t in $(targets) | ||
|  |     { | ||
|  |         target-index = [ CALC $(target-index) + 1 ] ; | ||
|  |         local appender-run = $(appender-runs) ; | ||
|  |         if $(targets[2])-defined | ||
|  |         { | ||
|  |             appender-run += [$(target-index)/$(target-count)] ; | ||
|  |         } | ||
|  |         append $(targets) : $(appender-run:J=" ") $(t) $(sources) ; | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | actions append | ||
|  | { | ||
|  |     $(ECHO_CMD)-------------------------------------------------$(X) | ||
|  |     $(ECHO_CMD)Appender run: $(>[1])$(X) | ||
|  |     $(ECHO_CMD)Appender run: $(>[1])$(X)>> "$(>[2])" | ||
|  |     $(ECHO_CMD)Target group: $(<:J=' ')$(X) | ||
|  |     $(ECHO_CMD)Target group: $(<:J=' ')$(X)>> "$(>[2])" | ||
|  |     $(ECHO_CMD)      Target: '$(>[2])'$(X) | ||
|  |     $(ECHO_CMD)      Target: '$(>[2])'$(X)>> "$(>[2])" | ||
|  |     $(ECHO_CMD)     Sources: '$(>[3-]:J=' ')'$(X) | ||
|  |     $(ECHO_CMD)     Sources: '$(>[3-]:J=' ')'$(X)>> "$(>[2])" | ||
|  |     $(ECHO_CMD)=================================================$(X) | ||
|  |     $(ECHO_CMD)-------------------------------------------------$(X)>> "$(>[2])" | ||
|  | } | ||
|  | """)
 | ||
|  | 
 | ||
|  | 
 | ||
|  | test_basic() | ||
|  | test_generated_target_names() |