code: def test_basic():
      t = BoostBuild.Tester()
      __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/"
          * 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/" * BoostBuild.List("c.my_obj " "auxilliary.my_lib")
      )
      t.expect_nothing_more()
  
      folder = "bin"
      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()
  
imports: import BoostBuild
  import re
label_desc: Use image node_id 1 for calling a global function with example usage: test_basic() without return types