#line 1874 "/home/travis/build/felix-lang/felix/src/packages/flx.fdoc"
  include "std/felix/flx/flx_depchk";
  include "std/felix/flx/flx_control";
  include "std/felix/flx/flx_depvars";
  
  gen dxqt(DBG:bool) (cmd:string) = {
    if DBG call fprintln (cstderr, "cmd="+cmd);
    var now = #Time::time;
    var result,output = Shell::get_stdout(cmd);
    if result == 0 do
      n :=
        match find_first_of (output, char "\n") with
        | Some n => n
        | #None => output.len
        endmatch
      ;
      output = output.[to n]; // first line excluding newline
      var elapsed = #Time::time - now;
      if DBG call fprintln (cstderr, "Popen:Elapsed: " + fmt (elapsed, fixed(9,3)) + ", output='"+output+"'");
    else
      if DBG call eprintln "COMMAND FAILED";
      fprint$ cstderr, ("Error "+repr(result)+" executing command " + cmd + "\n");
      System::pexit result;
    done
    return output;
  }
  
  proc xdebugln[T with Str[T]] (d:bool) (x:T) {
    if d call fprintln (cstderr, "[flx] " + str x);
  }
  
  // CLEAR_CACHE is set to 1 if the cache is reset
  proc check_cache(
    config:&Config::config_type,
    control:&FlxControl::control_type)
  {
    control*.CLEAR_CACHE, control*.cache_time = validate_cache (
      FLX_SHARE_DIR = config*.FLX_SHARE_DIR,
      AUTOMATON = control*.AUTOMATON,
      GRAMMAR_DIR = control*.GRAMMAR_DIR,
      STDGRAMMAR = control*.STDGRAMMAR,
      FLXG = control*.FLXG,
      CACHE_DIR = config*.FLX_CACHE_DIR,
      OUTPUT_DIR = config*.FLX_OUTPUT_DIR,
      CLEAR_CACHE= control*.CLEAR_CACHE,
      debugln = xdebugln[string] (control*.DEBUG_FLX),
      xqt = dxqt (control*.ECHO == 1 or control*.DEBUG_FLX),
      quote = Shell::quote_arg
    );
  }
  
  object processing_env(
    toolchain: clang_config_t -> toolchain_t,
    config:Config::config_type,
    var control:FlxControl::control_type,
    dvars:FlxDepvars::dvars_type)
  =
  {
    proc debugln[T with Str[T]] (x:T) {
      if control.DEBUG_FLX call fprintln (cstderr, "[flx] " + str x);
    }
  
    proc echoln[T with Str[T]] (x:T) {
      if control.ECHO == 1 call fprintln (cstderr, "[flx] " + str x);
    }
  
    var dflt_clang_config = (
        header_search_dirs = Empty[string],
        macros = Empty[string],
        library_search_dirs= Empty[string],
        ccflags= Empty[string],
        dynamic_libraries= Empty[string],
        static_libraries= Empty[string],
        debugln = debugln[string]
    );
  
    proc showtime(msg:string, t0:double)
    {
      if control.TIME == 1 do
        var elapsed = #Time::time - t0;
        var minutes = floor (elapsed / 60.0);
        var seconds = elapsed - minutes * 60.0;
        println$ "[flx] Time : " + fmt(minutes,fixed(2,0))+"m" + fmt(seconds,fixed(4,1)) + "s for " + msg;
      done
    }
  
  
    method gen system(cmd:string):int= {
      var now = #Time::time;
      if control.ECHO==1 do fprintln$ cstderr, cmd; done
      var result = System::system(cmd);
      var elapsed = #Time::time - now;
      if control.ECHO==1 do
        fprintln$ cstderr, "System:Elapsed: " + fmt (elapsed, fixed (8,3)) +
          ", Result code " + str(result)
        ;
      done
      return result;
    }
  
  //----------------------------------------------------------------------------
  // CALPACKAGES
  //----------------------------------------------------------------------------
  
    var calpackages_run = false;
  
    proc calpackages
    {
      debugln$ "[flx:calpackages] Calculating package requirements (calpackages_run="+str calpackages_run +")";
      if not calpackages_run  do
        var tc = toolchain dflt_clang_config;
        var x = FlxPkg::map_package_requirements
        (
           FLX_TARGET_DIR = config.FLX_TARGET_DIR,
           FLX_CONFIG_DIRS = config.FLX_CONFIG_DIRS,
           EXT_EXE = #(tc.executable_extension),
           EXT_STATIC_OBJ = #(tc.static_object_extension),
           EXT_DYNAMIC_OBJ = #(tc.dynamic_object_extension),
           STATIC = control.STATIC,
           LINKEXE = control.LINKEXE,
           SLINK_STRINGS = control.SLINK_STRINGS,
           DLINK_STRINGS = control.DLINK_STRINGS,
           LINKER_SWITCHES = control.LINKER_SWITCHES,
           cpp_filebase = dvars.cpp_filebase,
           EXTRA_PACKAGES = control.pkgs
        );
        //control.EXTRA_CCFLAGS = control.EXTRA_CCFLAGS + x.CFLAGS;
        control.CCFLAGS = control.CCFLAGS + x.CFLAGS;
        control.EXTRA_INCLUDE_FILES = x.INCLUDE_FILES;
        control.DRIVER_EXE = x.DRIVER_EXE;
        control.DRIVER_OBJS = x.DRIVER_OBJS;
        control.LINK_STRINGS = x.LINK_STRINGS;
        //println$ "LINK STRINGS = " + x.LINK_STRINGS;
        calpackages_run = true;
      done
    }
  
    fun find_cxx_pkgs (src:string) : list[string] =
    {
      debugln$ "[flx:find_cxx_pkgs] Scanning " + src + " for package requirements";
      var out = Empty[string];
      var pat = RE2('.*@requires package ([A-Za-z][A-Za-z0-9_-]*).*');
      var f = fopen_input_text src;
      if valid f do
        for line in f do
          var result = Match (pat,line);
          match result do
          | #None => ;
          | Some v => out = v.1  + out;
          done
        done
        fclose f;
      else
        eprintln("Can't find C++ source file " + src);
        System::exit(1);
      done
      out = rev out;
      if out != Empty[string] call
        eprintln$ "[flx] C++ file "+src+" requires packages " + str (out);
      return out;
    }
  
  //----------------------------------------------------------------------------
  // FELIX COMPILATION
  //----------------------------------------------------------------------------
  
    // max time of Felix source files: #FileStat::future_time if any missing
    fun cal_time_from_flxdepfile (debugln: string->0, df: string):double=
    {
      fun maxf (x: double) (f:string) =
      {
        if f == "" do return x; done
        var ext = Filename::get_extension f;
        var ft = if ext != "" then FileStat::dfiletime (f,#FileStat::past_time) else
          max (FileStat::dfiletime (f+".fdoc", #FileStat::past_time), FileStat::dfiletime (f+".flx",#FileStat::past_time))
        ;
        debugln$ ("Time "+f+" = "+ FileStat::strfiletime ft);
        ft = if ft == #FileStat::past_time then #FileStat::future_time else ft; // missing dependency
        return max (x,ft);
      }
  
      fun cal_files_time (fs: list[string])=> fold_left maxf #FileStat::past_time fs;
  
      var deptext = load_text df;
      var lines = split (deptext, "\n");
      debugln$ "Deps=" + str(lines);
      var deptime =
        let ft = cal_files_time lines in
        if ft == #FileStat::past_time then #FileStat::future_time else ft endif
      ;
      debugln$ "Deptime=" + FileStat::strfiletime(deptime);
      return deptime;
    }
  
    fun cal_cxx_uptodate(debugln:string -> 0, OUTPUT_DIR:string, f:string)=
    {
      val depfilename = cache_join (OUTPUT_DIR, f+".dep");
      debugln$ "Dependency file name = " + depfilename;
      var depfiletime = FileStat::dfiletime (depfilename, #FileStat::future_time);
      if depfiletime == #FileStat::future_time do
        debugln$ "Dependency file doesn't exist";
        return false;
      done
  
      var deptime = cal_time_from_flxdepfile (debugln, depfilename);
      debugln$ "dep time = " + FileStat::strfiletime deptime;
      debugln$ "depfile time = " + FileStat::strfiletime depfiletime;
      var cxx_uptodate = deptime < depfiletime;
      debugln$ "cxx generated by flxg is = " + if cxx_uptodate then "" else " NOT " endif + "uptodate";
      return cxx_uptodate;
    }
  
    gen check_cxx_uptodate () : bool =
    {
      debugln "Check Felix->C++ uptodate";
      if control.RECOMPILE == 1 do
        debugln$ "Felix->C++ dependency checking skipped due to switch RECOMPILE=1: forced not uptodate";
        return false;
      elif control.CHECK_DEPENDENCIES == 1 do
        debugln "Checking Felix->C++ dependencies since CHECK_DEPENDENCIES=1 to see if the cxx is uptodate";
        return cal_cxx_uptodate (debugln[string], config.FLX_OUTPUT_DIR, dvars.filebase);
      else
        debugln$ "Felix->C++ dependency checking skipped due to switch CHECK_DEPENDENCIES=0: forced uptodate";
        return true;
      done
    }
  
    gen run_felix_compiler_if_required () : int =
    {
      var result = 0;
      var uptodate = check_cxx_uptodate ();
      debugln$ "[run_felix_compiler_if_required] Uptodate=" + uptodate.str;
      if not uptodate do
        debugln$ "Running flxg because target is not uptodate";
        var t0 = #Time::time;
        result = Flxg::run_felix_compiler
        (
          INLINE=control.INLINE,
          OUTPUT_DIR=config.FLX_OUTPUT_DIR,
          BUNDLE_DIR=control.BUNDLE_DIR,
          CACHE_DIR=config.FLX_CACHE_DIR,
          COMPILER_PHASE= control.COMPILER_PHASE,
          DOREDUCE=control.DOREDUCE,
          FLXG = control.FLXG,
          VERBOSE = dvars.VERBOSE,
          // NOTE: BUG: Not passing grammar directory to compiler!
          // flxg expects file in standard library
          STDGRAMMAR = "@"+control.STDGRAMMAR,
          AUTOMATON = control.AUTOMATON,
          IMPORTS = control.STDIMPORT + control.IMPORTS,
          FLXLIBS = dvars.FLX_STD_LIBS,
          INCLUDE_DIRS = config.FLX_LIB_DIRS,
          filebase = dvars.filebase,
          use_ext = dvars.use_ext,
          TIME = control.COMPILER_TIME,
          FORCE = control.FLXG_FORCE,
          FLAGS = if control.FLXG_OPTIMISE == 0 then Empty[string] else list[string] "--optimise" endif,
          debugln = if control.ECHO==1 then echoln[string] else debugln[string] endif
        );
        showtime("Felix flxg   : "+dvars.cpp_filebase, t0);
        if result == 0 do
          debugln$ "Felix compilation succeeded";
          calpackages;
          FlxPkg::write_include_file(dvars.cpp_filebase, control.EXTRA_INCLUDE_FILES);
        done
      else
        debugln$ "skipping flxg because output is uptodate";
      done
      return result;
    }
  //----------------------------------------------------------------------------
  // C++ COMPILATION
  //----------------------------------------------------------------------------
  
    // C++ dynamic (one file)
    gen cxx_compile_dynamic1(src:string, dst:string) : int =
    {
      var t0 = #Time::time;
      var pkgs = find_cxx_pkgs src;
      control&.extra_pkgs <- control.extra_pkgs + pkgs;
      var pkg_cflags = Empty[string];
      if pkgs != Empty[string] do
        eprintln$ "[flx:cxx_compile_dynamic1] Adding packages " + str pkgs;
        var PKGCONFIG_PATH=map
           (fun (s:string) => "--path+="+s)
           config.FLX_CONFIG_DIRS
        ;
        var allargs = PKGCONFIG_PATH+"--field=cflags"+"--keepleftmost"+pkgs + control.pkgs;
        var ret,mycflags = FlxPkgConfig::flx_pkgconfig(allargs);
        if ret != 0 do
          eprintln$ "[flx:cxx_compile_dynamic1] Error " + str ret + " executing flx_pkgconfig, args=" + str allargs;
          System::exit (1);
        done
        pkg_cflags = mycflags;
      done
      var tc = toolchain
        extend dflt_clang_config with
        (
          ccflags = /* ccflags + */ control.CCFLAGS + pkg_cflags,
          header_search_dirs = config.FLX_RTL_DIRS+control.EXTRA_INCLUDE_DIRS,
          macros = control.MACROS,
          debugln = if control.ECHO==1 then echoln[string] else debugln[string] endif
        )
        end
      ;
      if control.RECOMPILE==1 or not cxx_depcheck (tc,src,dst) do
        var result = tc.cxx_dynamic_object_compiler (dst=dst,src=src);
        showtime("Dynamic c++  : "+src, t0);
        return result;
      else
        return 0;
      done
    }
  
    // C++ dynamic (many files)
    gen cxx_compile_dynamic () : int =
    {
      var EXT_SHARED_OBJ = #((toolchain dflt_clang_config).dynamic_object_extension);
      if
        control.CXXONLY == 0 and (
        control.LINKIT == 1 or
        control.OUTPUT_FILENAME_SPECIFIED == 0 and
        control.OUTPUT_FILENAME_WITHOUT_EXTENSION_SPECIFIED == 0)
      do
  //println$ "Compiling thunk";
        var result = cxx_compile_dynamic1
        (
          dvars.cpp_filebase+"_static_link_thunk.cpp",
          dvars.cpp_filebase+"_static_link_thunk"+EXT_SHARED_OBJ
        );
        if result != 0 return result;
      done
  
      if control.CXXONLY == 0 do
        if control.LINKIT == 0 do
          result = cxx_compile_dynamic1 (dvars.cpp_filebase+".cpp", control.LINKER_OUTPUT_FILENAME);
          if result != 0 return result;
        else
          result = cxx_compile_dynamic1 (dvars.cpp_filebase+".cpp", dvars.cpp_filebase+EXT_SHARED_OBJ);
          if result != 0 return result;
        done
      done
  
      for src in control.cpps do
        var dst = Filename::strip_extension src + EXT_SHARED_OBJ;
        result = cxx_compile_dynamic1 (src,dst);
        if result != 0 return result;
        *&control.cppos += dst;
      done
      return 0;
    }
  
    // C++ static (one file)
    gen cxx_compile_static () : int =
    {
      // we only need the thunk if we're linking OR -o switch was NOT specified
      // i.e. skip compiling the thunk the output name was specified and
      // represents an object file (or library archive?)
  //println$ "cxx_compile_static";
      var EXT_STATIC_OBJ = #((toolchain dflt_clang_config).static_object_extension);
      if
        control.CXXONLY == 0 and (
        control.LINKIT == 1 or
        control.OUTPUT_FILENAME_SPECIFIED == 0 and
        control.OUTPUT_FILENAME_WITHOUT_EXTENSION_SPECIFIED == 0)
      do
  //println$ "Compiling thunk";
        var result = cxx_compile_static1
        (
          dvars.cpp_filebase+"_static_link_thunk.cpp",
          dvars.cpp_filebase+"_static_link_thunk"+EXT_STATIC_OBJ
        );
        if result != 0 return result;
      done
  
      for src in control.cpps do
        var dst = Filename::strip_extension src +EXT_STATIC_OBJ;
        result = cxx_compile_static1 (src,dst);
        if result != 0 return result;
        *&control.cppos += dst;
      done
  
      if control.CXXONLY == 0 do
        if control.LINKIT == 0 do
    //println$ "Compile only " + control.LINKER_OUTPUT_FILENAME;
          // compile only
          return cxx_compile_static1 (dvars.cpp_filebase+".cpp",control.LINKER_OUTPUT_FILENAME);
        else
          // compile and link
    //println$ "Compile and link " + dvars.cpp_filebase+EXT_STATIC_OBJ;
          return cxx_compile_static1 (dvars.cpp_filebase+".cpp",dvars.cpp_filebase+EXT_STATIC_OBJ);
        done
      else
        return 0;
      done
    }
  
    // C++ static (many files)
    gen cxx_compile_static1 (src: string, dst: string) : int =
    {
  //println$ "cxx_compile_static1: " + src " -> " + dst;
      var t0 = #Time::time;
      var pkgs = find_cxx_pkgs src;
      control&.extra_pkgs <- control.extra_pkgs + pkgs;
      var pkg_cflags = Empty[string];
      if pkgs != Empty[string] do
        eprintln$ "[flx:cxx_compile_static1] Adding packages " + str pkgs;
        var PKGCONFIG_PATH=map
           (fun (s:string) => "--path+="+s)
           config.FLX_CONFIG_DIRS
        ;
        var allargs = PKGCONFIG_PATH+"--field=cflags"+"--keepleftmost"+pkgs+control.pkgs;
        var ret,mycflags = FlxPkgConfig::flx_pkgconfig(allargs);
        if ret != 0 do
          eprintln$ "[flx:cxx_compile_static1] Error " + str ret + " executing flx_pkgconfig, args=" + str allargs;
          System::exit (1);
        done
        pkg_cflags = mycflags;
      done
  
      var tc = toolchain
        extend dflt_clang_config with
        (
          ccflags = /*ccflags + */ control.CCFLAGS + pkg_cflags,
          header_search_dirs = config.FLX_RTL_DIRS+control.EXTRA_INCLUDE_DIRS,
          macros = control.MACROS,
          debugln = if control.ECHO==1 then echoln[string] else debugln[string] endif
        )
        end
      ;
      if control.RECOMPILE==1 or not cxx_depcheck (tc,src,dst) do
        var result = tc.cxx_static_object_compiler (dst=dst,src=src);
        showtime("Static c++   : "+src,t0);
        if result != 0 do
          eprintln$ "[flx] C++ compilation "+src+" failed";
        done
        return result;
      else
        return 0;
      done
  
    }
  
    // C++ (many files)
    gen run_cxx_compiler_if_required () : int =
    {
      var result = 0;
      if control.STATIC == 0 do
        debugln "Dynamic linkage";
        result = #cxx_compile_dynamic;
      else
        debugln "Static linkage";
        result = #cxx_compile_static;
      done
      return result;
    }
  /*
  
    gen check_run_if_required_and_uptodate() : bool  =
    {
  
      if control.RECOMPILE == 0 and control.RUNIT == 1 and control.CLEAR_CACHE == 0 do
        var uptodate = #check_cxx_uptodate and #check_binary_uptodate;
        if control.STATIC == 0 do
          if uptodate do
            debugln$ "Running dynamically linked binary";
            return true;
          else
            debugln$ "Dynamically linked binary out of date or non-existant";
          done
        else
          if uptodate do
            debugln$ "Running statically linked binary";
            return true;
          else
            debugln$ "Statically linked binary out of date or non-existant";
          done
        done
      done
      return false;
  
    }
    gen run_with_calpackages () : int =
    {
      if control.STATIC == 0 do
        return #run_dynamic_with_calpackages;
      else
        return #run_program_static;
      done
    }
  */
  
  //----------------------------------------------------------------------------
  // LINKAGE
  //----------------------------------------------------------------------------
  
    // ------------------------------------------------------------------
    // Link shared library (dll)
    // ------------------------------------------------------------------
    gen cxx_link_shared_library () : int =
    {
      var t0 = #Time::time;
      var pkg_dstrings= Empty[string];
      var pkgs = control.extra_pkgs;
      if pkgs != Empty[string] do
        eprintln$ "[flx:cxx_link_shared_library] Adding packages " + str pkgs;
        var PKGCONFIG_PATH=map
           (fun (s:string) => "--path+="+s)
           config.FLX_CONFIG_DIRS
        ;
        var allargs = PKGCONFIG_PATH+"-r"+"--field=provides_dlib"+"--field=requires_dlibs"+"--keepleftmost"+pkgs + control.pkgs;
        var ret,mydstrings = FlxPkgConfig::flx_pkgconfig(allargs);
        if ret != 0 do
          eprintln$ "[flx:cxx_link_shared_library] Error " + str ret + " executing flx_pkgconfig, args=" + str allargs;
          System::exit (1);
        done
        pkg_dstrings = mydstrings;
      done
  
      var tc = toolchain
        extend dflt_clang_config with
        (
          dynamic_libraries = control.LINK_STRINGS+pkg_dstrings, // a bit of a hack ..
          debugln = if control.ECHO==1 then echoln[string] else debugln[string] endif
        )
        end
      ;
      var EXT_SHARED_OBJ = #(tc.dynamic_object_extension);
      if control.CXXONLY == 0 do
        var result = tc.dynamic_library_linker
          (
            dst=control.LINKER_OUTPUT_FILENAME,
            srcs= control.cppos + (dvars.cpp_filebase+EXT_SHARED_OBJ)
          )
        ;
      else
        result = tc.dynamic_library_linker
          (
            dst=control.LINKER_OUTPUT_FILENAME,
            srcs= control.cppos
          )
        ;
      done
  
      showtime("Dynamic link : "+control.LINKER_OUTPUT_FILENAME,t0);
      if result != 0 do
        eprintln$ "[flx] C++ clink "+control.LINKER_OUTPUT_FILENAME+" failed";
      done
      return result;
    }
  
    gen cxx_link_shared_library_with_calpackages () : int =
    {
      calpackages;
      return #cxx_link_shared_library;
    }
  
    // ------------------------------------------------------------------
    // Link shared exe
    // ------------------------------------------------------------------
    gen cxx_link_shared_exe () : int =
    {
      var t0 = #Time::time;
      var pkg_dstrings= Empty[string];
      var pkgs = control.extra_pkgs;
      if pkgs != Empty[string] do
        eprintln$ "[flx:cxx_link_shared_exe] Adding packages " + str pkgs;
        var PKGCONFIG_PATH=map
           (fun (s:string) => "--path+="+s)
           config.FLX_CONFIG_DIRS
        ;
        var allargs = PKGCONFIG_PATH+"-r"+"--field=provides_dlib"+"--field=requires_dlibs"+"--keepleftmost"+pkgs + control.pkgs;
        var ret,mydstrings = FlxPkgConfig::flx_pkgconfig(allargs);
        if ret != 0 do
          eprintln$ "[flx:cxx_link_shared_exe] Error " + str ret + " executing flx_pkgconfig, args=" + str allargs;
          System::exit (1);
        done
        pkg_dstrings = mydstrings;
      done
      var tc = toolchain
        extend dflt_clang_config with
        (
          //ccflags = ccflags + control.CCFLAGS + control.LINK_STRINGS,
          dynamic_libraries = control.LINK_STRINGS + pkg_dstrings, // a bit of a hack
          debugln = if control.ECHO==1 then echoln[string] else debugln[string] endif
        )
        end
      ;
      println$ "Toolchain loaded " + #(tc.whatami);
  /*
  println$ "flx, prior to calling toolchain: DRIVER OBJS = " + control.DRIVER_OBJS.str;
  println$ "flx, prior to calling toolchain: cppos = " + control.cppos.str;
  */
      var EXT_DYNAMIC_OBJ = #(tc.dynamic_object_extension);
      if control.CXXONLY == 0 do
        var result = tc.dynamic_executable_linker
          (
            dst=control.LINKER_OUTPUT_FILENAME,
            srcs=
              control.DRIVER_OBJS +
              control.cppos +
              (dvars.cpp_filebase+"_static_link_thunk"+EXT_DYNAMIC_OBJ) +
              (dvars.cpp_filebase+EXT_DYNAMIC_OBJ)
          )
        ;
      else
        result = tc.dynamic_executable_linker
          (
            dst=control.LINKER_OUTPUT_FILENAME,
            srcs=
              control.cppos
          )
        ;
      done
      showtime("Dynamic executable link  : "+control.LINKER_OUTPUT_FILENAME,t0);
      if result != 0 do
        eprintln$ "[flx] C++ dynamic executable link "+control.LINKER_OUTPUT_FILENAME+" failed";
      done
      return result;
    }
  
    gen cxx_link_shared_exe_with_calpackages() :  int =
    {
      calpackages;
      return #cxx_link_shared_exe;
    }
  
    // ------------------------------------------------------------------
    // Link static exe
    // ------------------------------------------------------------------
    gen cxx_link_static_exe () : int =
    {
      var t0 = #Time::time;
      var pkg_sstrings= Empty[string];
      var pkgs = control.extra_pkgs;
      if pkgs != Empty[string] do
        eprintln$ "[flx:cxx_link_static] Adding packages " + str pkgs;
        var PKGCONFIG_PATH=map
           (fun (s:string) => "--path+="+s)
           config.FLX_CONFIG_DIRS
        ;
        var allargs = PKGCONFIG_PATH+"-r"+"--field=provides_slib"+"--field=requires_slibs"+"--keepleftmost"+pkgs + control.pkgs;
        var ret,mysstrings = FlxPkgConfig::flx_pkgconfig(allargs);
        if ret != 0 do
          eprintln$ "[flx:cxx_link_static] Error " + str ret + " executing flx_pkgconfig, args=" + str allargs;
          System::exit (1);
        done
        pkg_sstrings = mysstrings;
      done
      var tc = toolchain
        extend dflt_clang_config with
        (
          //ccflags = ccflags + control.CCFLAGS + control.LINK_STRINGS,
          static_libraries = control.LINK_STRINGS + pkg_sstrings, // a bit of a hack
          debugln = if control.ECHO==1 then echoln[string] else debugln[string] endif
        )
        end
      ;
      var EXT_STATIC_OBJ = #(tc.static_object_extension);
      if control.CXXONLY == 0 do
        var result = tc.static_executable_linker
          (
            dst=control.LINKER_OUTPUT_FILENAME,
            srcs=
              control.DRIVER_OBJS +
              control.cppos +
              (dvars.cpp_filebase+"_static_link_thunk"+EXT_STATIC_OBJ) +
              (dvars.cpp_filebase+EXT_STATIC_OBJ)
          )
        ;
      else
        result = tc.static_executable_linker
          (
            dst=control.LINKER_OUTPUT_FILENAME,
            srcs=
              control.cppos
          )
        ;
      done
      showtime("Static executable link  : "+control.LINKER_OUTPUT_FILENAME,t0);
      if result != 0 do
        eprintln$ "[flx] C++ static executable link "+control.LINKER_OUTPUT_FILENAME+" failed";
      done
      return result;
    }
  
    gen cxx_link_static_exe_with_calpackages() :  int =
    {
      calpackages;
      return #cxx_link_static_exe;
    }
  
    // ------------------------------------------------------------------
    // Link static (archive) library
    // ------------------------------------------------------------------
  
    gen cxx_static_library () : int =
    {
      var t0 = #Time::time;
      var tc = toolchain
        extend dflt_clang_config with
        (
          //ccflags = ccflags + control.CCFLAGS,
          debugln = if control.ECHO==1 then echoln[string] else debugln[string] endif
        )
        end
      ;
      var EXT_STATIC_OBJ = #(tc.static_object_extension);
      if control.CXXONLY == 0 do
        var result = tc . static_library_linker
          (
            srcs=control.cppos + (dvars.cpp_filebase+EXT_STATIC_OBJ) ,
            dst=control.LINKER_OUTPUT_FILENAME
          )
        ;
      else
        result = tc . static_library_linker
          (
            srcs=control.cppos,
            dst=control.LINKER_OUTPUT_FILENAME
          )
        ;
      done
      showtime("Static lib   : "+control.LINKER_OUTPUT_FILENAME,t0);
      if result != 0 do
        eprintln$ "[flx] C++ static library link "+control.LINKER_OUTPUT_FILENAME+" failed";
      done
      return result;
    }
  
  
  
    // Assumes C++ generated by flxg (using timestamp of dep file)
    // Assumes command line C++ file includes older than the argument (fixme!)
    gen check_binary_uptodate () : bool =
    {
      fun maxf (t:double) (f:string) => max (t, FileStat::dfiletime (f, #FileStat::future_time));
  
      debugln "Check C++->binary uptodate";
      if control.RECOMPILE == 1 do
        debugln$ "C++->binary dependency checking skipped due to switch RECOMPILE=1: forced not uptodate";
        return false;
      elif control.CHECK_DEPENDENCIES == 1 do
        debugln "Checking C++->binary dependencies since CHECK_DEPENDENCIES=1 to see if the output is uptodate";
  
        var xtime = FileStat::dfiletime(control.LINKER_OUTPUT_FILENAME,#FileStat::past_time);
        val depfilename = cache_join (config.FLX_OUTPUT_DIR, dvars.filebase+".dep");
        var flx_srctime = FileStat::dfiletime (depfilename,#FileStat::future_time);
        var cpp_srctime = fold_left maxf #FileStat::past_time control.cpps;
        var obj_srctime = fold_left maxf #FileStat::past_time control.cppos;
        var deptime = max (max (flx_srctime, cpp_srctime), obj_srctime);
        var uptodate = xtime > deptime;
  
  
        debugln$ "Extra c++ sources  "+ str control.cpps;
        debugln$ "Extra object files "+ str control.cppos;
  
        debugln$ "Filebase = " + dvars.filebase;
  
        debugln$ "cache   time = " + FileStat::strfiletime (control.cache_time);
        debugln$ "flx_src time = " + FileStat::strfiletime (flx_srctime);
        debugln$ "cpp_src time = " + FileStat::strfiletime (cpp_srctime);
        debugln$ "obj_src time = " + FileStat::strfiletime (obj_srctime);
  
        debugln$ "dep     time = " + FileStat::strfiletime (deptime);
        debugln$ "Binary  time = " + FileStat::strfiletime (xtime) + " for " + control.LINKER_OUTPUT_FILENAME;
        debugln$ "output is " + if uptodate then "" else " NOT " endif + " up to date";
        return uptodate;
      else
        debugln$ "C++->binary dependency checking skipped due to switch CHECK_DEPENDENCIES=0: forced uptodate";
        return true;
      done
    }
  
  
    gen run_linker_if_required() : int =
    {
      var result = 0;
      if control.CCOMPILEIT == 0 do
        debugln "C++ compilation (and linking and running) skipped by switch";
      else
        var uptodate = #check_binary_uptodate;
        if uptodate do
          debugln "Linking skipped because binary is uptodate";
        else
          if control.STATIC == 0 do
            debugln "Dynamic linkage";
            if control.LINKEXE == 1 do
              result = #cxx_link_shared_exe_with_calpackages;
            else
              result = #cxx_link_shared_library_with_calpackages;
            done
          else
            debugln "Static linkage";
            if control.STATICLIB == 1 do
              result = #cxx_static_library;
            else
              result = #cxx_link_static_exe_with_calpackages;
            done
          done
        done
      done
      return result;
    }
  
  
  
  /*
    method gen runit() : int = {
      var immediate_run = #check_run_if_required_and_uptodate;
      if immediate_run do
        debugln$ "Uptodate so run immediately";
        return #run_with_calpackages;
      else
        var result = #run_felix_compiler_if_required;
        if result != 0 return result;
        return #run_cxx_and_exe_as_required;
      done
    }
  */
  //----------------------------------------------------------------------------
  // EXECUTION
  //----------------------------------------------------------------------------
  
    gen run_program_dynamic () : int =
    {
      var result = 0;
      if control.CXXONLY == 0 do
        var xargs =
          control.DRIVER_EXE +
          dvars.DEBUGSWITCH +
          control.LINKER_OUTPUT_FILENAME +
          dvars.args
        ;
        var CMD = strcat ' ' control.FLXRUN + ' ' + catmap ' ' Shell::quote_arg xargs;
        if control.STDOUT != "" do CMD=CMD+" > " +Shell::quote_arg(control.STDOUT); done
        if control.STDIN != "" do CMD=CMD+" < " +Shell::quote_arg(control.STDIN); done
        debugln$ "Run command="+CMD;
        var t0 = #Time::time;
        result = system(CMD);
        showtime("Dynamic Run : "+control.LINKER_OUTPUT_FILENAME,t0);
      else
        println$ "Cannot run C++ dynamic library " + control.LINKER_OUTPUT_FILENAME;
      done
      return result;
    }
  
    gen run_program_static () : int =
    {
      var result = 0;
      var CMD =
        catmap ' ' Shell::quote_arg ( dvars.STATIC_ENV + control.LINKER_OUTPUT_FILENAME + dvars.args )
      ;
  
      if control.STDOUT != "" do CMD=CMD + " > "+Shell::quote_arg(control.STDOUT); done
      if control.STDIN != "" do CMD=CMD+" < " +Shell::quote_arg(control.STDIN); done
      debugln$ "Run command="+CMD;
      var t0 = #Time::time;
      result=system(CMD);
      showtime("Static Run   : "+control.LINKER_OUTPUT_FILENAME,t0);
      return result;
    }
  
  
    gen run_dynamic_with_calpackages () : int =
    {
      calpackages;
      return #run_program_dynamic;
    }
  
    gen run_program_if_required () : int =
    {
      var result = 0;
      if control.STATIC == 0 do
        debugln$ "Running dynamic program";
        result = #run_dynamic_with_calpackages;
      else
        // NOTE: since Felix sets environment variable for plugin loads ..
        // doesn't even a static program need calpackages?
        debugln$ "Running static program";
        result = #run_program_static;
      done
      return result;
    }
  //----------------------------------------------------------------------------
  // OUTPUT VERIFICATION
  //----------------------------------------------------------------------------
  
    gen check_output_if_required () : int =
    {
      var result = 0;
      var expected = control.EXPECT;
      var output = control.STDOUT;
  
      // possible bug in flx, if either missing it should have been
      // set by default based on program name
      if output == "" do
        eprintln$ "[flx] No output file given??";
        result = 1;
      elif expected == "" do
        eprintln$ "[flx] No expect file given??";
        result = 1;
      else
  
        // note load never fails, at worse loads empty string.
        var output_text = load_text (output);
        var expected_text = load_text (expected);
        var bresult = output_text == expected_text;
        if not bresult do
          eprintln$ "[flx] Output " + output + " doesn't match expected " + expected;
          result = 1;
        done
      done
      return result;
    }
  //----------------------------------------------------------------------------
  // ORDER OF OPERATION
  //----------------------------------------------------------------------------
  
    method gen runit() : int = {
      var result = 0;
      if control.FELIX == 1 do
        result = #run_felix_compiler_if_required;
        if result != 0 return result;
      else
        debugln$ "Felix compilation skipped by switch";
      done
  
      // we should run this on demand? And split up calculations
      // for driver (needed to run dynamic program) and headers etc
      // (needed after flxg to complete C++ code gen) and link stuff
      // (needed for linkage)
      calpackages;
      if control.LINKER_OUTPUT_FILENAME != "" do
         Directory::mkdirs (Filename::dirname control.LINKER_OUTPUT_FILENAME);
      done
  
      if control.CCOMPILEIT == 1 do
        result = #run_cxx_compiler_if_required;
        if result != 0 return result;
      else
        debugln "C++ compilation (and linking and running) skipped by switch";
      done
  
      if control.LINKIT == 1 do
        result = #run_linker_if_required;
        if result != 0 return result;
      else
        debugln "Link step skipped by switch";
      done
  
      if control.RUNIT == 1 do
        result = #run_program_if_required;
        if result != 0 return result;
      else
        debugln "Running program skipped by switch";
      done
  
      if control.EXPECT != "" do
        result = #check_output_if_required;
        if result != 0 return result;
      done
      return result;
    }
  
  }