Package: src/packages/buildtools.fdoc

Tools For building Felix

key file
flx_build_flxg.flx $PWD/src/tools/flx_build_flxg.flx
flx_build_prep.flx $PWD/src/tools/flx_build_prep.flx
flx_build_rtl.flx $PWD/src/tools/flx_build_rtl.flx
flx_build_boot.flx $PWD/src/tools/flx_build_boot.flx
build_boot.fpc $PWD/src/config/build_boot.fpc

Tools to build the core Felix system.

These tools are written in Felix and can be used to build a Felix installation. However of course you must first have a working installation to do this.

Building the compiler flxg

Run this one first to build the compiler. It is built directly from the repository.

//[flx_build_flxg.flx]
class BuildFlxg
{


  // remove slosh-newline
  fun pack(s:string) = {
    var slosh = false;
    var space = true;
    var out = "";
    for ch in s do
      if ch == char "\n" and slosh do slosh = false;
      elif ch == char "\\" do slosh=true;
      elif slosh do slosh=false; out+="\\"; out+=ch;
      elif ch == "\t" do out+=char ' '; space=true;
      elif ch == ' ' and space do ;
      elif ch == ' ' do out+=ch; space=true;
      else out+=ch; space=false;
      done
    done
    return out;
  }

  fun version_hook () = {
    var time = #Time::time;
    //var fmttime = fmt_time (time, "%a %d %b %Y");
    var fmttime = time.str; // Its just arbitrary text
    return
      "open Flx_version\n" +
      "let version_data: version_data_t = \n" +
      "{\n" +
      '  version_string = "' + Version::felix_version + '";\n' +
      '  build_time_float = '+ str time + ';\n'+
      '  build_time = "' + fmttime + '";\n'+
      "}\n" +
      ";;\n" +
      "let set_version () = \n" +
      "  Flx_version.version_data := version_data\n" +
      ";;\n"
    ;
  }

  fun first (a:string, b:string) => a;
  fun second (a:string, b:string) => b;
  proc build_flx_drivers()
  {
    var tmpdir = 'build/flxg-tmp';
    fun entmp (a:string) => if prefix (a,tmpdir) then a else tmpdir/a;

    C_hack::ignore$ Directory::mkdir tmpdir;

    // make the version hook file
    begin
      var path = tmpdir/"flx_version_hook";
      Directory::mkdirs path;
      var f = fopen_output (path/"flx_version_hook.ml");
      write (f, #version_hook);
      fclose f;
    end

    var db = strdict[bool]();
    typedef db_t = strdict[bool];

    var sorted_libs = Empty[string];

    fun libdflt () => (
      srcs=Empty[string],
      libs=Empty[string],
      includes=Empty[string],
      external_libs=Empty[string]
    );

    typedef libspec_t = typeof (#libdflt);

    fun exedflt () => libdflt();
    typedef exespec_t = typeof (#exedflt);

    fun lexdflt () => (flags=Empty[string]);
    typedef lexspec_t = typeof (#lexdflt);

    fun yaccflt () => (flags=Empty[string]);
    typedef yaccspec_t = typeof (#lexdflt);

    fun dypgendflt () => (flags=Empty[string]);
    typedef dypgenspec_t = typeof (#dypgendflt);

    gen ocamldep (dir:string, src:string) = {
      var result, dep = Shell::get_stdout$ list$ "ocamldep.opt", "-native","-I", Filename::dirname src, "-I", dir, "-I", tmpdir, src;
      if result != 0 do
        println$ "Ocamldep failed to process " + src;
        System::exit (1);
      done
      //println$ "Ocamldep raw return = " + dep;
      var out = dep.pack.strip;
      //println$ "Ocamldep packed return = " + out;
      var lines = filter (fun (s:string) => stl_find (s,".cmo") == stl_npos) (split(out,"\n"));
      //println$ "Ocamldep lines = " + str lines;
      var res = head lines;
      //println$ "ocamldep result=" + res;
      var pos = stl_find (res, ":");
      if pos == stl_npos do
        println$ "Cannot find ':' in string " + res;
        System::exit 1;
      done
      res = res.[pos+2 to].strip;
      //println$ "ocamldep result 2 =" + res;
      var dfiles = split(res,' ');
      //println$ "ocamldep result 3 =" + str dfiles;
      dfiles = unbox (map (fun (s:string) = { //println$ "Extension swap case '" + s+"'";
        match Filename::get_extension s with
        | ".cmi" => return Filename::strip_extension s + ".mli";
        | ".cmx" => return Filename::strip_extension s + ".ml";
        | "" => return "";
        | x => return  "ERROR" ;
        endmatch;
        })
        dfiles)
      ;
      //println$ "ocamldep result 4 =" + str dfiles;
      dfiles = filter (fun (s:string) => s != "") dfiles;
      return dfiles;
    }

    variant build_kind = Library | Executable;

    gen ocaml_build(kind:build_kind, dir:string, lib:string, spec:libspec_t) =
    {
      var safe_string_flag =
        if lib == "dypgen.exe"
        then "-unsafe-string"
        else "-safe-string"
      ;
      println$ "-" * 20;
      println$ "Lib=" + lib + " in " + dir;
      println$ "Safe-string-flag=" + safe_string_flag;
      println$ "-" * 20;
      //println$ "srcs = \n    " +strcat "\n    " spec.srcs;
      println$ "libs= \n    " + strcat "\n    " spec.libs;
      println$ "includes= \n" + strcat "\n    " spec.includes;
      /*
      println$ "external libs = \n    " + strcat "\n    " spec.external_libs;
      println$ "-" * 20;
      println$ "";
      */

      // copy the list of files, processing dyp, mll, and mly files we encounter.
      var infiles = spec.srcs;
      var files = Empty[string];
      for file in infiles do
        match Filename::get_extension file with
        | ".mli" => files += file;
        | ".ml" => files += file;
        | ".dyp" => files += dypgen file;
        | ".mll" => files += ocamllex file;
        | ".mly" => var out = ocamlyacc file; files += out+".ml"; files += out+".mli";
        endmatch;
      done

      var sorted_files = Empty[string];
      begin
        // calculate dependencies
        var db = strdict[list[string]]();
        for file in files do
          var deps = ocamldep (dir,file);
          deps = filter (fun (f:string) => f in files) deps;
          db.add file deps;
          //println$ "Ocamldep : " + src + " : " + str deps;
        done

        // topological sort
        var count = 0;
        while not files.is_empty do
          ++count;
          if count > 40 do
            println$ "Invalid file or circular reference";
            System::exit 1;
          done
          var unsorted = Empty[string];
          for file in files do
            match db.get file with
            | Some dps =>
              if dps \subseteq sorted_files do
                sorted_files = file + sorted_files;
              else
                unsorted = file + unsorted;
              done
            | #None => assert false;
            endmatch;
          done
          files = unsorted;
        done
        sorted_files = unbox (rev sorted_files);
        //println$ "Library build order: " + str sorted_files;
      end

      // compile the files
      var include_flags = fold_left (fun (acc:list[string]) (a:string) => acc+"-I"+entmp a) Empty[string] spec.libs;
      for file in sorted_files do
        var path = tmpdir/(Filename::dirname file);
        Directory::mkdirs path;
        match Filename::get_extension file with
        | ".mli" =>
          println$ "Compiling MLI " + file;
          begin
            var result = Shell::system$ list(
               "ocamlc.opt",
               "-I",tmpdir,
               "-I",tmpdir/dir,
               "-I", entmp (Filename::dirname file)) +
               include_flags + safe_string_flag +
               list("-c", "-w",'yzex','-warn-error',"FPSU",
               '-o',entmp (Filename::strip_extension file) + ".cmi",
               file)
            ;
            if result != 0 do
              println$ "MLI Compile Failed : " + file;
              System::exit 1;
            done
          end
        | ".ml" =>
          println$ "Compiling ML  " + file;
          begin
            var result = Shell::system$ list(
               "ocamlopt.opt",
               "-I",tmpdir,
               "-I",tmpdir/dir,
               "-I", entmp (Filename::dirname file)) +
               include_flags + safe_string_flag +
               list("-c", "-w",'yzex','-warn-error',"FPSU",
               '-o',entmp (Filename::strip_extension file) + ".cmx",
               file)
            ;
            if result != 0 do
              println$ "ML Compile Failed : " + file;
              System::exit 1;
            done
          end
        | x => println$ "Ignoring " + file;
        endmatch;
      done

      match kind with
      | #Library =>
        begin
          // link files into library
          println$ "Linking library " + tmpdir/lib + ".cmxa";
          sorted_libs = sorted_libs + (tmpdir/lib+ ".cmxa");
          var result = Shell::system$ "ocamlopt.opt" + list(
            "-a", "-w",'yzex','-warn-error',"FPSU",
            '-o',tmpdir/lib + ".cmxa") +
            unbox (map
              (fun (s:string) => entmp (Filename::strip_extension s) + ".cmx")
              (filter (fun (s:string)=> Filename::get_extension s == ".ml") sorted_files)
          );
          if result !=0 do
            println$ "Linking cmxa library " + tmpdir/lib+'.cmxa' + " failed";
            System::exit 1;
          done
        end
      | #Executable =>
        begin
          // link files into executable
          println$ "Linking executable " + tmpdir/lib;
          var result = Shell::system$ "ocamlopt.opt" + list(
             "-w",'yzex','-warn-error',"FPSU",
            '-o',tmpdir/lib ) + spec.external_libs + sorted_libs +
            unbox (map
              (fun (s:string) => entmp (Filename::strip_extension s) + ".cmx")
              (filter (fun (s:string)=> Filename::get_extension s == ".ml") sorted_files)
            )
          ;
          if result !=0 do
            println$ "Linking executable " + tmpdir/lib+ " failed";
            System::exit 1;
          done
        end
      endmatch;

      // return the directory containing the library source.
      return dir;
    }

    gen ocaml_build_lib (dir:string, lib:string, spec:libspec_t) =>
      ocaml_build(Library,dir,lib,spec)
    ;

    gen ocaml_build_exe (dir:string, lib:string, spec:libspec_t) =>
      ocaml_build(Executable,dir,lib,spec)
    ;


    // src, including .mll suffix, dst: including .ml suffix
    gen ocamllex (file:string) : string =
    {
      var out = entmp (file.Filename::basename.Filename::strip_extension + ".ml");
      var result = Shell::system$ list$ 'ocamllex.opt','-o',out,file;
      if result != 0 do
        println$ "Ocamllex failed to process " + file;
        System::exit (1);
      done
      return out;
    }

    // src, including .mly suffix, dst: excluding suffices
    gen ocamlyacc(file:string) : string =
    {
      var out = entmp (file.Filename::basename.Filename::strip_extension);
      var result = Shell::system('ocamlyacc.opt','-b'+out,file);
      if result != 0 do
        println$ "Ocamlyacc failed to process " + file;
        System::exit (1);
      done
      return out;
    }

    // executable: the dypgen executable name
    // src: including .dyp suffix
    // tmpdir: directory for target .ml, .mli files
    gen dypgen(file:string) : string =
    {
      var flags = list$ "--no-mli", "--no-undef-nt", "--pv-obj", "--noemit-token-type";
      var executable = tmpdir / 'dypgen.exe';

      // Dypgen doesn't allow an output spec
      // so we process a copy of the file.
      var dyp = entmp (file.Filename::basename);
      C_hack::ignore$ FileSystem::filecopy (file, dyp);
      var result = Shell::system(executable + flags +  dyp);
      if result != 0 do
        println$ "dypgen failed to process " +file;
        System::exit (1);
      done
      return dyp.Filename::strip_extension+".ml";
    }

    gen build_dypgen() =
    {
      var path = 'src'/'compiler'/'dypgen'/'dypgen';
      var exe = ocaml_build_exe (path,'dypgen.exe',
         extend #libdflt with (srcs=mls_nodyp path,
            libs = list[string] (build_dyplib())
            ) end);
      println$ "Done, exe = " + exe;
      return exe;
    }
    //----------------------------------------------------------------------------------

    fun / (a:string, b:string) => Filename::join (a,b);

    gen mls (d:string) = {
      var files = FileSystem::regfilesin (d, RE2 '.*\\.(mli?|dyp|mll|mly)');
      return unbox (map (fun (f:string) = { return d/f;}) files);
    }

    gen mls_nodyp (d:string) = {
      var files = FileSystem::regfilesin (d, RE2 '.*\\.(mli?|mll|mly)');
      return unbox (map (fun (f:string) = { return d/f;}) files);
    }


    gen build_ocs() =
    {
      var path = ('src'/'compiler'/'ocs'/'src');
      if db.haskey path do return path; done
      db.add path true;
      return ocaml_build_lib(path, 'ocs',
          extend #libdflt with (srcs=mls path) end);
    }

    gen build_sex() =
    {
      var path = ('src'/'compiler'/'sex');
      if db.haskey path do return path; done
      db.add path true;
      return ocaml_build_lib(path, 'sex',
          extend #libdflt with (srcs=mls path,
          libs=list[string] (build_dyplib(), build_ocs())) end);
    }

    gen build_dyplib() =
    {
      var path = ('src'/'compiler'/'dypgen'/'dyplib');
      if db.haskey path do return path; done
      db.add path true;

      return ocaml_build_lib(path, 'dyp',
          extend #libdflt with (srcs=mls path) end);
    }

    gen build_flx_version() = {
        var path = ('src'/'compiler'/'flx_version');
        if db.haskey path do return path; done
        db.add path true;

        return ocaml_build_lib(path, 'flx_version',
            extend #libdflt with (srcs=mls path) end);
    }

    gen build_flx_misc() = {
        var path = 'src'/'compiler'/'flx_misc';
        if db.haskey path do return path; done
        db.add path true;
        return ocaml_build_lib(path, 'flx_misc',
            extend #libdflt with (srcs=mls path,
            libs=list[string] (build_flx_version()),
            external_libs=list[string]('str', 'unix')) end);
    }

    gen build_flx_version_hook() = {
        var path = tmpdir/'flx_version_hook';
        if db.haskey path do return path; done
        db.add path true;
        return ocaml_build_lib(path, 'flx_version_hook',
            extend #libdflt with (srcs=mls path,
            libs=list[string](build_flx_version())) end);
    }

    gen build_flx_lex() = {
        var path = 'src'/'compiler'/'flx_lex';
        if db.haskey path do return path; done
        db.add path true;
        return ocaml_build_lib(path,'flx_lex',
            extend #libdflt with (srcs=mls path,
            libs=list[string](
                build_dyplib(),
                build_ocs(),
                build_sex(),
                build_flx_version())) end);
    }

    gen build_flx_parse() = {
        var path = 'src'/'compiler'/'flx_parse';
        if db.haskey path do return path; done
        db.add path true;
        return ocaml_build_lib(path,'flx_parse',
            extend #libdflt with (srcs=mls path,
            libs=list[string](
                build_dyplib(),
                build_ocs(),
                build_sex(),
                build_flx_version(),
                build_flx_lex())) end);
    }

    gen build_flx_file() = {
        var path = 'src'/'compiler'/'flx_file';
        if db.haskey path do return path; done
        db.add path true;
        return ocaml_build_lib(path,'flx_file',
            extend #libdflt with (srcs=mls path,
            libs=list[string](
                build_dyplib(),
                build_ocs(),
                build_sex(),
                build_flx_version(),
                build_flx_misc(),
                build_flx_lex(),
                build_flx_parse()
                )) end);
    }

    gen build_flx_core() = {
        var path = 'src'/'compiler'/'flx_core';
        if db.haskey path do return path; done
        db.add path true;
        return ocaml_build_lib(path, 'flx_core',
            extend #libdflt with (srcs=mls path,
            libs=list[string](
                build_dyplib(),
                build_ocs(),
                build_flx_lex(),
                build_flx_parse(),
                build_flx_misc()
                ),
            external_libs=list[string]()) end);
    }

    gen build_flx_desugar() = {
        var path = 'src'/'compiler'/'flx_desugar';
        if db.haskey path do return path; done
        db.add path true;

        return ocaml_build_lib(path, 'flx_desugar',
            extend #libdflt with (srcs=mls path,
            libs=list[string](
                build_dyplib(),
                build_ocs(),
                build_sex(),
                build_flx_lex(),
                build_flx_parse(),
                build_flx_file(),
                build_flx_misc(),
                build_flx_core(),
                build_flx_version()
                ),
            external_libs=list[string]('unix')) end);
    }

    gen build_flx_bind() = {
        var path = 'src'/'compiler'/'flx_bind';
        if db.haskey path do return path; done
        db.add path true;
        return ocaml_build_lib(path, 'flx_bind',
            extend #libdflt with (srcs=mls path,
            libs=list[string](
                build_flx_lex(),
                build_flx_misc(),
                build_flx_core(),
                build_flx_desugar()),
            external_libs=list[string]()) end);
    }

    gen build_flx_frontend() = {
        var path = 'src'/'compiler'/'flx_frontend';
        if db.haskey path do return path; done
        db.add path true;
        return ocaml_build_lib(path, 'flx_frontend',
            extend #libdflt with (srcs=mls path,
            libs=list[string](
                build_flx_lex(),
                build_flx_misc(),
                build_flx_core())) end);
    }

    gen build_flx_opt() = {
        var path = 'src'/'compiler'/'flx_opt';
        if db.haskey path do return path; done
        db.add path true;
        return ocaml_build_lib(path, 'flx_opt',
            extend #libdflt with (srcs=mls path,
            libs=list[string](
                build_flx_lex(),
                build_flx_misc(),
                build_flx_core(),
                build_flx_frontend())) end);
    }

    gen build_flx_lower() = {
        var path = 'src'/'compiler'/'flx_lower';
        if db.haskey path do return path; done
        db.add path true;
        return ocaml_build_lib(path, 'flx_lower',
            extend #libdflt with (srcs=mls path,
            libs=list[string](
                build_flx_lex(),
                build_flx_misc(),
                build_flx_core(),
                build_flx_frontend())) end);
    }

    gen build_flx_backend() = {
        var path = 'src'/'compiler'/'flx_backend';
        if db.haskey path do return path; done
        db.add path true;
        return ocaml_build_lib(path, 'flx_backend',
            extend #libdflt with (srcs=mls path,
            libs=list[string](
                build_flx_lex(),
                build_flx_misc(),
                build_flx_core())) end);
    }

    gen build_flx_cpp_backend() = {
        var path = 'src'/'compiler'/'flx_cpp_backend';
        if db.haskey path do return path; done
        db.add path true;
        return ocaml_build_lib(path, 'flx_cpp_backend',
            extend #libdflt with (srcs=mls path,
            libs=list[string](
                build_flx_lex(),
                build_flx_misc(),
                build_flx_core(),
                build_flx_frontend(),
                build_flx_backend()),
            external_libs=list[string]()) end);
    }

    println$ "Build dypgen";
    C_hack::ignore$ build_dypgen();
    var libs = list (
          build_ocs(),
          build_sex(),
          build_dyplib(),
          build_flx_version(),
          build_flx_lex(),
          build_flx_parse(),
          build_flx_misc(),
          build_flx_file(),
          build_flx_core(),
          build_flx_desugar(),
          build_flx_bind(),
          build_flx_frontend(),
          build_flx_opt(),
          build_flx_lower(),
          build_flx_backend(),
          build_flx_cpp_backend(),
          build_flx_version_hook()
    );

    var external_libs = list('unix.cmxa', 'str.cmxa');
    C_hack::ignore$ libs;
    var path ='src'/'compiler'/'flxg';
    var exe = ocaml_build_exe (path,'flxg',
            extend #libdflt with (srcs=mls path,
            libs = libs,
            external_libs=external_libs) end);
    println$ "Done, exe = " + exe;
  } // end build_drivers
} // end class


BuildFlxg::build_flx_drivers();

Preparation for building.

This tools copies things out of the repository and sets up the build target directory.

//[flx_build_prep.flx]
include "std/felix/flx_cp";

class FlxPrepBuild
{

  fun / (x:string,y:string) => Filename::join(x,y);

  proc dirsetup(cmd:cmd_type)
  {
    // NOTE: unlink doesn't work on directories anyhow ...
    // We need rmdir(), but that doesn't work unless dir is empty!
    //FileSystem::unlink("trial-tmp");

    if cmd.clean_target_dir do
       println$ "Deleting target-dir=" + cmd.target_dir;
       FileSystem::unlink(cmd.target_dir);
    elif cmd.clean_target_bin_dir do
       println$ "Deleting target-bin=" + cmd.target_dir/cmd.target_bin;
       FileSystem::unlink(cmd.target_dir/cmd.target_bin);
    elif cmd.clean_target_bin_binaries do
      println$ "Cleaning binaries out of target not implemented";
    done

    C_hack::ignore$ Directory::mkdir(cmd.target_dir);
    C_hack::ignore$ Directory::mkdir(cmd.target_dir/cmd.target_bin);
    C_hack::ignore$ Directory::mkdir(cmd.target_dir/cmd.target_bin/'bin');

    // Set up the share subdirectory.
    if cmd.copy_repo do
      if cmd.repo != cmd.target_dir/'share' do
        println$ "Copy repository "+cmd.repo/'src -> ' + cmd.target_dir/'share'/'src';
        CopyFiles::copyfiles(cmd.repo/'src',
         '(.*\.(h|hpp|ml|mli|c|cpp|cxx|cc|flx|flxh|fdoc|fsyn|js|html|css|svg|png|gif|jpg|files|include|ttf))',
         cmd.target_dir/'share'/'src'/'${1}',true,cmd.debug);
      else
        println$ "Cannot copy repo because source = target";
      done
    done

    if cmd.copy_library do
      println$ "Copy Felix library";
      CopyFiles::copyfiles (cmd.target_dir/'share'/'src'/'lib', r"(.*\.(flx|flxh|fsyn|fdoc|files))",
        cmd.target_dir/'share'/'lib/${1}',true,cmd.debug);
    done

    // This is SPECIAL because "version.flx" is the only file which is both
    // shared-readonly and generated. So it has to be copied out of an
    // existing built library not the repository dir.
    // TODO: generate it using, say, flx or flxg.
    if cmd.copy_version do
      if cmd.source_dir != cmd.target_dir do
        CopyFiles::copyfiles (cmd.source_dir/'share'/'lib'/'std', '(version.flx)',
          cmd.target_dir/'share'/'lib'/'std/${1}',true,cmd.debug);
      else
        println$ "Cannot copy version because source = target";
      done
    done

    if cmd.copy_pkg_db do
      if cmd.source_dir/cmd.source_bin != cmd.target_dir/cmd.target_bin do
        println$ "Copy config db";
        CopyFiles::copyfiles(cmd.source_dir/cmd.source_bin/'config', '(.*)',
          cmd.target_dir/cmd.target_bin/'config'/'${1}',true,cmd.debug);
      else
        println$ "Cannot copy config db because source = target";
      done
    done

    if cmd.copy_config_headers do
      if cmd.source_dir/cmd.source_bin != cmd.target_dir/cmd.target_bin do
        println$ "Copy rtl config headers";
        CopyFiles::copyfiles(cmd.source_dir/cmd.source_bin/'lib', r"(.*\.(h|hpp|flx|flxh))",
          cmd.target_dir/cmd.target_bin/'lib'/'${1}',true,cmd.debug);
      else
        println$ "Cannot copy rtl config headers because source = target";
      done
    done

    // configure and db copy are exclusive
    if cmd.configure do
      println$ 'Generating Configuration';
      if cmd.compiler not in ('gcc', 'clang', 'msvc') do
        println$ 'ERROR: Configuration compiler must be gcc,clang or msvc';
        System::exit 1;
      done
      if cmd.os not in ('linux', 'macosx', 'win') do
        println$ 'ERROR: Configuration os must be linux,macosx or win';
        System::exit 1;
      done
      if cmd.bits not in ('32', '64') do
        println$ 'ERROR: Configuration bits musty be 32 or 64';
        System::exit 1;
      done
      if cmd.os == 'win' and cmd.bits == '32' do
        println$ 'ERROR: Only 64 bit windows is supported';
        System::exit 1;
      done

      // setup fpc's to copy: ORDER MATTERS!
      var fpcs = ([ '([^/]*\\.fpc)']);
      if cmd.os in ('linux','macosx') do
        fpcs +=  'unix'/'([^/]*\\.fpc)';
        fpcs +=  ('unix'+cmd.bits)/'([^/]*\\.fpc)';
      done
      fpcs += cmd.os/'([^/]*\\.fpc)';
      fpcs += (cmd.os+cmd.bits)/'([^/]*\\.fpc)';
      fpcs += cmd.os/'([^/]*\\.fpc)';

      // Felix platform macro
      var fmacs = ([cmd.os+'/([^/]*\\.flxh)']);

      // setup header files to copy
      // os/bits/compiler config
      var headers = ([(cmd.os+cmd.bits)/cmd.compiler/'rtl'/'([^/]*\\.hpp)']);
      // socket config
      headers += (cmd.os+cmd.bits)/'rtl'/'([^/]*\\.hpp)';

      // do the copying
      println$ 'Copying fpcs ..';
      for pattern in fpcs perform
        CopyFiles::copyfiles(cmd.repo/'src'/'config', pattern,
          cmd.target_dir/cmd.target_bin/'config'/'${1}',true,cmd.debug);
      println$ 'Copying platform macro ..';
      for pattern in fmacs perform
        CopyFiles::copyfiles(cmd.repo/'src'/'config', pattern,
          cmd.target_dir/cmd.target_bin/'lib'/'plat'/'${1}',true,cmd.debug);
      println$ 'Copying C++ headers ..';
      for pattern in headers perform
        CopyFiles::copyfiles(cmd.repo/'src'/'config', pattern,
          cmd.target_dir/cmd.target_bin/'lib'/'rtl'/'${1}',true,cmd.debug);

      if cmd.c_compiler != "" do begin
        println$ 'Specifying C compiler executable ' + cmd.c_compiler;
        var fn = cmd.target_dir/cmd.target_bin/'config'/"toolchain_"+cmd.compiler+"_"+cmd.os+"_c_compiler_executable.fpc";
        var txt = (["compiler: " + cmd.c_compiler]);
        save (fn,txt);
      end done

      if cmd.cxx_compiler != "" do begin
        println$ 'Specifying C++ compiler executable ' + cmd.cxx_compiler;
        var fnam = cmd.target_dir/cmd.target_bin/'config'/"toolchain_"+cmd.compiler+"_"+cmd.os+"_cxx_compiler_executable.fpc";
        var txt = (["compiler: " + cmd.cxx_compiler]);
        save (fnam,txt);
      end done
    done

    if cmd.setup_pkg != "" do
      var setupdata = load cmd.setup_pkg;
      var commands = split(setupdata,"\n");
      var lineno = 0;
      for command in commands do
        //println$ "Command=" + command;
        ++lineno;
        var hsrc, hdst = "","";
        match split (command, ">") with
        | #Empty => ;
        | Cons (h,#Empty) => hsrc = strip h;
        | Cons (h,Cons (d,#Empty)) => hsrc = strip h; hdst = strip d;
        | _ =>
           println$ "[flx_build_prep:setup-pkg] file too many > characters file: "+
           cmd.setup_pkg +"["+lineno.str+"] " + command;
        endmatch;

        if hsrc != "" do
          if hdst == "" do hdst = "${0}"; done
          println$ "Copying files " + hsrc + " > " + hdst;
          //println$ "From source directory " + cmd.source_dir;
          //println$ "To target directory " + cmd.target_dir/cmd.target_bin;
          CopyFiles::copyfiles (cmd.source_dir, hsrc,cmd.target_dir/cmd.target_bin/hdst,true, true);
        done
      done
    done
  }

  proc flx_build(cmd: cmd_type)
  {
    dirsetup(cmd);
    // copy the compiler
    var compiler_name = "flxg";
    if PLAT_WIN32 do
       compiler_name += ".exe";
    done
    if cmd.copy_compiler call CopyFiles::copyfiles(cmd.source_dir/cmd.source_bin/'bin', compiler_name,
      cmd.target_dir/cmd.target_bin/'bin'/'flxg', true, cmd.debug);

    println$ "Build Complete";
  }

  proc print_help()
  {
    println$ "flx_build_prep v2018.09.22";
    println$ "Usage: flx_build_prep ";
    println$ "";
    println$ "# locations";
    println$ "";
    println$ "  --repo=repo                 default: src";
    println$ "  --target-dir=target_dir     default: build/trial";
    println$ "  --target-bin=target_bin     default: host";
    println$ "  --source-dir=source_dir     default: build/release";
    println$ "  --source-bin=source_bin     default: host";
    println$ "";
    println$ "# configuration";
    println$ "";
    println$ "  --configure                 generate configuration";
    println$ "  --compiler=(gcc/clang/msvc) no default!";
    println$ "  --os=(linux/macosx/win)     no default!";
    println$ "  --bits=(32/64)              defaut 64";
    println$ "  --c-compiler=               default std compiler name";
    println$ "  --cxx-compiler=             default std compiler name";

    println$ "";
    println$ "# cleaning options";
    println$ "";
    println$ "  --clean-target-dir          delete entire target directory";
    println$ "  --clean-target-bin-dir      delete target sub-directory";
    println$ "  --clean-target-bin-binaries delete binaries from target sub-directory (not implemented yet)";
    println$ "";
    println$ "# copy options";
    println$ "";
    println$ "  --copy-repo                 copy src dir of repository";
    println$ "  --copy-compiler             copy compiler flxg";
    println$ "  --copy-pkg-db               copy package database";
    println$ "  --copy-config-headers       copy C++ config headers (NO LONGER OF ANY USE!)";
    println$ "  --copy-version              copy Felix version file";
    println$ "  --copy-library              copy Felix library";
    println$ "";
    println$ "# selective setup of pkg-db";
    println$ "  --setup=pkg                 setup using file";
    println$ "  --toolchain=toolchain       specify toolchain to use";
    println$ "  --debug                     do stuff verbosely";
    println$ "";
    println$ "# Environment variables";
    println$ "";
    println$ "FLX_SHELL_ECHO=1              echo all shell callouts (system, popen)";
    println$ "FLX_DEBUG_FLX=1               make 'flx' explain its processing decisions";
    println$ "BUILD_FLX_TOOLCHAIN_FAMILY=family   family=gcc or family=clang";
    println$ "";
    println$ "Purpose: setup new Felix target";
    println$ "";
    println$ "Requires repository directory $repo contain subdirectory 'src'";
    println$ "Requires directory $source_dir contain subdirectory $source_bin which contains program 'flxg'";
    println$ "Ensures target_dir contains:";
    println$ "";
    println$ "  (a) Repository source in $target_dir/share/src";
    println$ "  (b) config db, C++ headers, libraries in $target_dir/$target_bin/*";
    println$ "";
    println$ "Copies version, flxg, config db, and C++ headers from $source_dir if required";
  }

  proc setup_toolchain(var toolchain:string, pkgdir:string)
  {
    // if the toolchain is specified, fix it
    if toolchain != "" do
      begin
        println$ "Write toolchain " + toolchain + " into package " + pkgdir/'toolchain.fpc';
        Directory::mkdirs pkgdir;
        var f = fopen_output (pkgdir/'toolchain.fpc');
        write (f,"toolchain: " + toolchain +"\n");
        fclose f;
      end
      println$ "WRITING SPECIFIED TOOLCHAIN PACKAGE: ****************************";
    elif FileStat::fileexists (pkgdir/'toolchain.fpc') do
      println$ "USING EXISTING TOOLCHAIN PACKAGE: ****************************";
    else // guess toolchain and write it
      var res, os = Shell::get_stdout("uname");
      &os <- os.strip;
      var compiler_family = Env::getenv "BUILD_FLX_TOOLCHAIN_FAMILY";
      match os,compiler_family do
      | "","" => &toolchain <- "toolchain_mscv_win";
      | "Linux","" => &toolchain <- "toolchain_gcc_linux";
      | "Darwin","" => &toolchain <- "toolchain_clang_macosx";

      | "Linux","gcc" => &toolchain <- "toolchain_gcc_linux";
      | "Linux","clang" => &toolchain <- "toolchain_clang_linux";
      | "Darwin","gcc" => &toolchain <- "toolchain_gcc_macosx";
      | "Darwin","clang" => &toolchain <- "toolchain_clang_macosx";

      | _,_ =>
        println$ "No toolchain specified in toolchain.fpc or with --toolchain switch";
        println$ "  uname returns unknown OS: '" +os+'"';
        println$ "Either:";
        println$ "  (1) Set environment variable BUID_FLX_TOOLCHAIN_FAMILY=family where family=gcc or family=clang";
        println$ "  (2) Set the toolchain.fpc file to read 'toolchain:toolchain_name";
        println$ "  (3) use --toolchain=toolchain_name command line option";
        println$ "  Note:toolchain name is form 'toolchain_<family>_<os>'";
        println$ "    where os=Darwin or os=Linux or os=Win32";
        System::exit(1);
      done
      begin
        println$ "Write toolchain " + toolchain + " into package " + pkgdir/'toolchain.fpc';
        var f = fopen_output (pkgdir/'toolchain.fpc');
        write (f,"toolchain: " + toolchain +"\n");
        fclose f;
      end
      println$ "USING GUESSED TOOLCHAIN PACKAGE: ****************************";
    done
    println$ load (pkgdir/'toolchain.fpc');
  }

  typedef cmd_type = typeof (parse_args Empty[string]);

  noinline fun parse_args (args: list[string]) =
  {
     var cmd = (
       repo = '.',
       target_dir="build"/"trial",
       target_bin="host",
       source_dir="build"/"release",
       source_bin="host",
       toolchain="",

       clean_target_dir=false,
       clean_target_bin_dir=false,
       clean_target_bin_binaries=false,

       copy_repo=false,
       copy_compiler=false,
       copy_pkg_db=false,
       copy_config_headers=false,
       copy_version=false,
       copy_library=false,
       setup_pkg="",
       configure=false,
       compiler="notspecified",
       os="notspecified",
       bits="notspecified",
       c_compiler="",
       cxx_compiler="",
       debug = false
     );

     for arg in args do
       // location options
       if prefix(arg,"--repo=") do
         &cmd.repo <- arg.[7 to];
       elif prefix(arg,"--target-dir=") do
         &cmd.target_dir <- arg.[13 to];
       elif prefix(arg,"--target-bin=") do
         &cmd.target_bin <- arg.[13 to];
       elif prefix(arg,"--source-dir=") do
         &cmd.source_dir <- arg.[13 to];
       elif prefix(arg,"--source-bin=") do
         &cmd.source_bin <- arg.[13 to];
       elif prefix(arg,"--toolchain=") do
         &cmd.toolchain <- arg.[12 to];
       elif arg == "--debug" do
         &cmd.debug <- true;

       // operation options: cleaning
       elif arg == "--clean-target-dir" do
         &cmd.clean_target_dir <- true;
       elif arg == "--clean-target-bin-dir" do
         &cmd.clean_target_bin_dir <- true;
       elif arg == "--clean-target-bin-binaries" do
         &cmd.clean_target_bin_binaries <- true;

       // operation options: copying
       elif arg == "--copy-repo" do
         &cmd.copy_repo<- true;
       elif arg == "--copy-compiler" do
         &cmd.copy_compiler<- true;
       elif arg == "--copy-pkg-db" do
         &cmd.copy_pkg_db <- true;
       elif arg == "--copy-config-headers" do
         &cmd.copy_config_headers <- true;
       elif arg == "--copy-version" do
         &cmd.copy_version <- true;
       elif arg == "--copy-library" do
         &cmd.copy_library <- true;

       // configuration
       elif prefix(arg,"--configure") do
         &cmd.configure <-true;
       elif prefix(arg,"--compiler") do
         &cmd.compiler<- arg.[11 to];
       elif prefix(arg,"--os=") do
         &cmd.os<- arg.[5 to];
       elif prefix(arg,"--bits=") do
         &cmd.bits<- arg.[7 to];
       elif prefix(arg,"--c-compiler=") do
         &cmd.c_compiler<- arg.[13 to];
       elif prefix(arg,"--cxx-compiler=") do
         &cmd.cxx_compiler<- arg.[15 to];

       // special configuration package
       elif prefix(arg,"--setup=") do
         &cmd.setup_pkg <- arg.[8 to];

       // help
       elif arg == "--help" do
         print_help();
         System::exit(0);
       else
         println$ "Unknown switch " + arg;
         print_help();
         System::exit(1);
       done
     done


     return cmd;
  }

  noinline proc build_felix (xargs:list[string])
  {
    if xargs.len.int < 2 do
      print_help();
      System::exit(1);
    done
    var cmd = parse_args (tail xargs);
    println$ "flx_build_prep v2018.09.22";
    println$ "  repository       = " + cmd.repo;
    println$ "  target-dir       = " + cmd.target_dir;
    println$ "  target-bin       = " + cmd.target_bin;
    println$ "  source-dir       = " + cmd.source_dir;
    println$ "  source-bin       = " + cmd.source_bin;
    if cmd.configure do
      println$ "CONFIGURE compiler=" + cmd.compiler+", os=" + cmd.os + ", bits=" + cmd.bits;
      cmd&.toolchain <- "toolchain_" + cmd.compiler + "_" + cmd.os;
    done
    println$ "  setup-pkg        = " + cmd.setup_pkg;
    println$ "  toolchain (spec) = " + cmd.toolchain;
    flx_build (cmd);
    var target_config_dir = cmd.target_dir/cmd.target_bin/"config" ;
    setup_toolchain(cmd.toolchain,target_config_dir );
  }

}

FlxPrepBuild::build_felix (#System::args);

System::exit (0);

Build the Run Time Library (RTL)

Builds the run time library from the build target share directory. Does not look in the repository.

//[flx_build_rtl.flx]
include "std/felix/toolchain_config";
include "std/felix/toolchain_interface";
include "std/felix/flx_pkgconfig";
include "std/felix/flx_pkg"; // only for "fix2word_flags"
include "std/felix/flx_cp";
include "std/felix/flx/flx_depchk";
include "std/pthread/threadpool";
include "std/felix/flx_mklib";

class FlxRtlBuild
{

  private fun / (x:string,y:string) => Filename::join(x,y);

  proc ehandler () {
    eprintln$ "Flx_buildtools:FlxRtlBuild flx_pkgconfig temporary ehandler invoked";
    System::exit 1;
  }


  proc make_rtl (
    build:string, target:string,
    boot_package:string,
    tmpdir:string,
    static_only:bool,
    noexes:bool,
    debug: bool
  )
  {
    val pkgdir = build / target / 'config';
    val srtl = build / 'share' / 'lib' / 'rtl';
    val hrtl = build / target / 'lib' / 'rtl';
    val bin = build / target / 'bin';
    val repo = build / 'share'; // excludes "src" cause that's in the packages

    proc dbug (x:string) => if debug call println$ '[make_rtl] ' + x;
    Directory::mkdirs tmpdir;
    Directory::mkdirs hrtl;
    Directory::mkdirs srtl;
    println$ "bootpkg=" + boot_package + " build image=" + build;

    var db = FlxPkgConfig::FlxPkgConfigQuery (list[string] pkgdir);

    gen getbootfield (field:string) => db.getpkgfield1 ehandler (boot_package, field);
    // toolchain pkg 1
    var toolchain_name = db.getpkgfield1 ehandler ("toolchain","toolchain");

    var c_compiler_executable =
      db.getpkgfielddflt ehandler (toolchain_name+"_c_compiler_executable", "compiler")
    ;

    var cxx_compiler_executable =
      db.getpkgfielddflt ehandler (toolchain_name+"_cxx_compiler_executable", "compiler")
    ;


    println$ "toolchain    : " + str toolchain_name + ", c: "+ c_compiler_executable + ", c++: " + cxx_compiler_executable;

    var allpkgs = db.getclosure ehandler boot_package;
    //println$ "Closure      : " + str allpkgs;

    for pkg in allpkgs begin
      var lib = db.getpkgfielddflt ehandler (pkg,"library");
      var srcdir = db.getpkgfielddflt ehandler (pkg,"srcdir");
      println$ f"%15S %20S %20S" (pkg,lib,srcdir);
    end

    var toolchain-maker =
      Dynlink::load-plugin-func1 [toolchain_t,toolchain_config_t]
      (
        dll-name=toolchain_name,
        setup-str="",
        entry-point=toolchain_name
      )
    ;
    for pkg in allpkgs begin
      var library = db.getpkgfielddflt ehandler (pkg,"library");
      var srcdir = db.getpkgfielddflt ehandler (pkg,"srcdir");
      var src = db.getpkgfield ehandler (pkg,"src");
      if library != "" do
        if srcdir == "" do
          println$ "Package error, package " + pkg + " library " + library + " No srcdir specified";
          System::exit(1);
        done
        if src.is_empty do
          println$ "Package error, package " + pkg + " library " + library + " No src files specified";
          System::exit(1);
        done
        var src_dir =  build / 'share';
        var share_rtl = src_dir / 'lib' / 'rtl';
        var target_dir =  build / target / 'lib' / 'rtl';
        var result = FlxLibBuild::make_lib (db,toolchain-maker, c_compiler_executable, cxx_compiler_executable,src_dir, target_dir, share_rtl, pkg,tmpdir, static_only, debug) ();
        if not result do
          eprintln$ "Library build " + pkg + " failed";
          System::exit 1;
        done
      else
        println$ "------------";
        println$ "External package " + pkg;
        println$ "------------";
      done
    end

    // make drivers
    begin
      println$ "------------";
      println$ "Make drivers";
      println$ "------------";
      var srcdir = repo/"src"/"flx_drivers";
      var toolchain_config =
        (
          c_compiler_executable = c_compiler_executable,
          cxx_compiler_executable = cxx_compiler_executable,
          header_search_dirs= list[string] (hrtl, srcdir, srtl),
          macros= Empty[string],
          ccflags = Empty[string],
          library_search_dirs= list[string] ("-L"+hrtl),
          dynamic_libraries= Empty[string],
          static_libraries= Empty[string], //############ FIXME or the link won't work!
          debugln = dbug
        )
      ;
      fun prgname (file:string) => let
          dstprg = file.Filename::strip_extension + #(toolchain.executable_extension) in
          bin / dstprg
      ;

      var toolchain = toolchain-maker toolchain_config;
      println$ #(toolchain.whatami);
      proc cobj_static (s:string,dst:string) {
        var src = srcdir/s;
        println$ "Compiling [static] " + src + " -> " + dst;
        var fresh = cxx_depcheck (toolchain, src, dst);
        var result = if fresh then 0 else
          toolchain.cxx_static_object_compiler(src=src, dst=dst)
        ;
        if result != 0 do
          println$ "Driver compile "+ s + " -> " + dst +" FAILED";
          System::exit 1;
        done
      }
      proc cobj_dynamic (s:string,dst:string) {
        var src = srcdir/s;
        if static_only do
          println$ "Skipping [dynamic] " + src + " -> " + dst + " due to flag";
        else
          println$ "Compiling [dynamic] " + src + " -> " + dst;
          var fresh = cxx_depcheck (toolchain, src, dst);
          var result = if fresh then 0 else
            toolchain.cxx_dynamic_object_compiler(src=src, dst=dst)
          ;
          if result != 0 do
            println$ "Driver compile "+ s + " -> " + dst +" FAILED";
            System::exit 1;
          done
        done
      }

      // VERY CONFUSING!
      // This one is for full static linkage, RTL static linked
      cobj_static("flx_run_lib_static.cpp",hrtl/"flx_run_lib_static"+#(toolchain.static_object_extension));

      // This run is for linking an executable which uses the RTL dynamic linked
      cobj_dynamic("flx_run_lib_static.cpp",hrtl/"flx_run_lib_static"+#(toolchain.dynamic_object_extension));

      // This one is for loading a program as a DLL, i.e. for use in flx_run.exe
      cobj_dynamic("flx_run_lib_dynamic.cpp",hrtl/"flx_run_lib_dynamic"+#(toolchain.dynamic_object_extension));

      cobj_static("flx_arun_lib_static.cpp",hrtl/"flx_arun_lib_static"+#(toolchain.static_object_extension));
      cobj_dynamic("flx_arun_lib_static.cpp",hrtl/"flx_arun_lib_static"+#(toolchain.dynamic_object_extension));
      cobj_dynamic("flx_arun_lib_dynamic.cpp",hrtl/"flx_arun_lib_dynamic"+#(toolchain.dynamic_object_extension));

      cobj_static("flx_run_main.cxx",hrtl/"flx_run_main"+#(toolchain.static_object_extension));
      cobj_dynamic("flx_run_main.cxx",hrtl/"flx_run_main"+#(toolchain.dynamic_object_extension));

      cobj_static("flx_arun_main.cxx",hrtl/"flx_arun_main"+#(toolchain.static_object_extension));
      cobj_dynamic("flx_arun_main.cxx",hrtl/"flx_arun_main"+#(toolchain.dynamic_object_extension));

      proc prg(file:string) {
        var exe = prgname file;
        println$ "Linking [executable] " + exe;
        var objs = list (
          hrtl/file+"_lib_dynamic"+#(toolchain.dynamic_object_extension),
          hrtl/file+"_main"+#(toolchain.dynamic_object_extension)
        );
        var result,libs = db.query$ list("--rec","--keeprightmost",
          "--field=provides_dlib","--field=requires_dlibs",file);
        libs = FlxPkg::fix2word_flags libs;
        if result != 0 do
          println$ "Driver pkgconfig query for "+ file+" FAILED";
          System::exit 1;
        done
        if noexes do
          println$ "Skipping executable link due to flag";
        else
          result = toolchain.dynamic_executable_linker(srcs=objs+libs, dst=exe);
          if result != 0 do
            println$ "Driver link  "+ file+" FAILED";
            System::exit 1;
          done
        done
      }
      prg("flx_run");
      prg("flx_arun");
    end
  }

  proc flx_build(cmd: cmd_type)
  {
    make_rtl ( cmd.target_dir, cmd.target_bin, cmd.boot_package, cmd.tmp_dir, cmd.static_only, cmd.noexes, cmd.debug);
    println$ "Build Complete";
  }

  proc print_help()
  {
    println$ "Usage: flx_build_rtl ";
    println$ "";
    println$ "# locations";
    println$ "";
    println$ "  --pkg=bootpkg (default: flx_rtl_core)";
    println$ "  --target-dir=target_dir     default: build/trial";
    println$ "  --target-bin=target_bin     default: host";
    println$ "  --tmp-dir=tmp               default: build/rtl-tmp";
    println$ "  --static                    static link libraries only";
    println$ "  --noexes                    libraries only";
    println$ "";
    println$ "  --debug                     do stuff verbosely";
    println$ "";
    println$ "# Environment variables";
    println$ "";
    println$ "FLX_SHELL_ECHO=1              echo all shell callouts (system, popen)";
    println$ "FLX_DEBUG_FLX=1               make 'flx' explain its processing decisions";
    println$ "";
    println$ "Purpose: Build new Felix target";
    println$ "";
    println$ "Ensures target_dir contains:";
    println$ "";
    println$ "  (a) Repository source in $target_dir/share/src";
    println$ "  (b) Share library in $target_dir/share/lib";
    println$ "  (c) config db, C++ headers, libraries and executables in $target_dir/$target_bin/*";
    println$ "";
    println$ "Compiles all C++ sources to libraries and executables";
  }

  typedef cmd_type = typeof (parse_args Empty[string]);

  noinline fun parse_args (args: list[string]) =
  {
     var cmd = (
       boot_package="",
       target_dir="build"/"trial",
       target_bin="host",
       tmp_dir="build"/"rtl-tmp",
       static_only=false,
       noexes=false,
       debug = false
     );

     for arg in args do
       // location options
       if prefix(arg,"--pkg=") do
         &cmd.boot_package <- arg.[6 to];
       elif prefix(arg,"--target-dir=") do
         &cmd.target_dir <- arg.[13 to];
       elif prefix(arg,"--target-bin=") do
         &cmd.target_bin <- arg.[13 to];
       elif prefix(arg,"--tmp-dir=") do
         &cmd.tmp_dir <- arg.[10 to];
       elif arg == "--static" do
         &cmd.static_only <- true;
       elif arg == "--noexes" do
         &cmd.noexes<- true;
       elif arg == "--debug" do
         &cmd.debug <- true;

       elif arg == "--help" do
         print_help();
         System::exit(0);
       else
         println$ "Unknown switch " + arg;
         print_help();
         System::exit(1);
       done
     done
     if cmd.boot_package== "" perform &cmd.boot_package <- "flx_rtl_core";
     return cmd;
  }

  noinline proc build_felix_rtl (xargs:list[string])
  {
    if xargs.len.int < 2 do
      print_help();
      System::exit(1);
    done
    var cmd = parse_args (tail xargs);
    println$ "flx_build_rtl v1.9";
    println$ "  build-package = " + cmd.boot_package;
    println$ "  target-dir    = " + cmd.target_dir;
    println$ "  target-bin    = " + cmd.target_bin;
    println$ "  tmp-dir       = " + cmd.tmp_dir;
    println$ "  static only   = " + cmd.static_only.str;
    println$ "  no executables= " + cmd.noexes.str;
    flx_build (cmd);
  }

}

FlxRtlBuild::build_felix_rtl (#System::args);

System::exit (0);

Build everything else.

Builds the plugins and essential build tools including flx and flx_pkgconfig and all the build tools in this package.

It uses a specified build configuration file to determine what to build. The standard file is build_boot.fpc in the configuration directory.

//[build_boot.fpc]
web_plugin:      cpp2html
web_plugin:      fdoc2html
web_plugin:      fdoc_edit
web_plugin:      fdoc_button
web_plugin:      fdoc_fileseq
web_plugin:      fdoc_heading
web_plugin:      fdoc_paragraph
web_plugin:      fdoc_scanner
web_plugin:      fdoc_slideshow
web_plugin:      toc_menu
web_plugin:      fdoc_frame
web_plugin:      flx2html
web_plugin:      fpc2html
web_plugin:      ocaml2html
web_plugin:      py2html
toolchain_plugin:      toolchain_clang_linux
toolchain_plugin:      toolchain_clang_macosx
toolchain_plugin:      toolchain_iphoneos
toolchain_plugin:      toolchain_iphonesimulator
toolchain_plugin:      toolchain_gcc_linux
toolchain_plugin:      toolchain_gcc_macosx
toolchain_plugin:      toolchain_msvc_win
tool:      flx_cp
tool:      flx_ls
tool:      flx_grep
tool:      flx_replace
tool:      flx_batch_replace
tool:      flx_tangle
tool:      flx_perror
tool:      flx_gramdoc
tool:      flx_libindex
tool:      flx_libcontents
tool:      flx_mktutindex
tool:      flx_renumber
tool:      flx_iscr
tool:      flx_pretty
flx_tool: flx_find_cxx_packages
flx_tool: flx_gen_cxx_includes
flx_tool: flx_pkgconfig
flx_tool: flx_build_prep
flx_tool: flx_build_rtl
flx_tool: flx_build_boot
flx_tool: flx_build_flxg
//[flx_build_boot.flx]
include "std/felix/toolchain_config";
include "std/felix/toolchain_interface";
include "std/felix/flx_cp";
include "std/felix/flx_pkgconfig";
include "std/felix/flx_pkg"; // only for "fix2word_flags"
include "std/felix/flx/flx_plugin_client";

class FlxCoreBuild
{

  fun / (x:string,y:string) => Filename::join(x,y);

  proc ehandler () {
    eprintln$ "Flx_buildtools:FlxCoreBuild flx_pkgconfig temporary ehandler invoked";
    System::exit 1;
  }


  proc build_plugins(target_dir:string, target_bin:string, plugins:list[string])
  {
    for plugin in plugins do
      println$ "Building plugin " + plugin;
      var result = Flx_client::runflx$ list ('[flx]',
        '--test='+target_dir, '--target='+target_bin,
        '-c', '-ox',target_dir/target_bin/'lib'/'rtl'/plugin,
        target_dir/'share'/'lib'/'plugins'/plugin);
      if result != 0 do
        println$ "plugin (dynamic) build failed";
        System::exit 1;
      done

      result = Flx_client::runflx$ list ('[flx]',
        '--test='+target_dir, '--target='+target_bin,
        '-c', '--nolink','-ox', target_dir/target_bin/'lib'/'rtl'/plugin,
        target_dir/'share'/'lib'/'plugins'/plugin);
      if result != 0 do
        println$ "plugin (dynamic obj) build failed";
        System::exit 1;
      done

      result = Flx_client::runflx$ list ('[flx]',
        '--test='+target_dir, '--target='+target_bin,
        '--static','-c', '--nolink','-ox', target_dir/target_bin/'lib'/'rtl'/plugin,
        target_dir/'share'/'lib'/'plugins'/plugin);
      if result != 0 do
        println$ "plugin (static obj) build failed";
        System::exit 1;
      done
    done

  }

  proc build_exes(target_dir:string, target_bin:string, tools:list[string])
  {
    println$ "build exes";
    for exe in tools do
      var src = Filename::join ("tools",exe);
      println$ src + " -> " + exe;
      var result = Flx_client::runflx$ list ('[flx]',
        '--test='+target_dir, '--target='+target_bin,
        '--static','-c',
        '-ox', target_dir/target_bin/'bin'/exe, target_dir/'share'/'src'/src);
      if result != 0 do
        println$ "exe build failed";
        System::exit 1;
      done
    done
  }

  proc build_flx_tools (target_dir:string, target_bin:string, tools:list[string])
  {
    println$ "build flx build tools";
    for exe in tools do
      var src = Filename::join ("tools",exe);
      println$ src + " -> " + exe;
      var result = Flx_client::runflx$ list ('[flx]',
        '--test='+target_dir, '--target='+target_bin,
        '--static','-c',
        '-ox', target_dir/target_bin/'bin'/exe, target_dir/'share'/'src'/src);
      if result != 0 do
        println$ "exe build failed";
        System::exit 1;
      done
    done
  }

  proc build_flx_web (target_dir:string, target_bin:string, web_plugins:list[string])
  {
    if PLAT_WIN32 do
      var obj_extn = "_static.obj"; // HACK!!!!!!!!
    else
      var obj_extn = "_static.o"; // HACK!!!!!!!!
    done

    println$ "dflx_web  -> dflx_web object file";
    var result = Flx_client::runflx$ list ('[flx]',
      '--test='+target_dir, '--target='+target_bin,
      '--static','-c','--nolink',
      '-o', target_dir/target_bin/'lib'/'rtl'/'dflx_web'+obj_extn, target_dir/'share'/'src'/'tools'/'dflx_web');
    if result != 0 do
      println$ "dflx_web build failed";
      System::exit 1;
    done
    var web_plugin_objs =
      unbox (map
        (fun (s:string) => target_dir/target_bin/'lib'/'rtl'/s+obj_extn)
        web_plugins
      )
    ;

    println$ "Build flx_web. Note: requires --build-web-plugins";
    println$ "flx_web  -> flx_web executable";
    result = Flx_client::runflx$
      list (
        '[flx]',
        '--test='+target_dir, '--target='+target_bin,
        '--static','-c',
        '-ox', target_dir/target_bin/'bin'/'flx_web') +
      web_plugin_objs +
      list (
        target_dir/target_bin/'lib'/'rtl'/'dflx_web' + obj_extn,
        target_dir/'share'/'src'/'tools'/'flx_web.flx')
    ;
    if result != 0 do
      println$ "exe build failed";
      System::exit 1;
    done
  }

  proc build_flx (target_dir:string, target_bin:string, toolchain_plugins:list[string])
  {
    if PLAT_WIN32 do
      var obj_extn = ".obj"; // HACK!!!!!!!!
    else
      var obj_extn = ".o"; // HACK!!!!!!!!
    done
    println$ "dflx  -> dflx object file";
    var result = Flx_client::runflx$ list ('[flx]',
      '--test='+target_dir, '--target='+target_bin,
      '-c','--nolink', '--static',
      '-o', target_dir/target_bin/'lib'/'rtl'/'dflx'+obj_extn, target_dir/'share'/'src'/'tools'/'dflx');
    if result != 0 do
      println$ "dflx build failed";
      System::exit 1;
    done

    println$ "Compile of dflx"+obj_extn+" SUCCEEDED";

    var toolchain_objects : list[string] = unbox (map (fun (p:string) =>
      target_dir/target_bin/'lib'/'rtl'/p + "_static"+obj_extn)
      toolchain_plugins)
    ;

    println$ "Linking dflx"+obj_extn+" with toolchains "+toolchain_objects.str;

    println$ "Build flx. Note: requires --build-toolchain-plugins";
    println$ "flx  -> flx";
    result = Flx_client::runflx$ list ('[flx]',
      '--test='+target_dir, '--target='+target_bin,
      '--static','-c',
      '-ox', target_dir/target_bin/'bin'/'flx') + toolchain_objects +
      (target_dir/target_bin/'lib'/'rtl'/'dflx' + obj_extn) +
      (target_dir/'share'/'src'/'tools'/'flx.flx')
    ;
    if result != 0 do
      println$ "exe build failed";
      System::exit 1;
    done
    println$ "Build flx: SUCCEEDED";
  }

  proc flx_build(cmd: cmd_type)
  {
    println$ "bootpkg=" + cmd.boot_package;
    var pkgdir = Filename::join (cmd.target_dir, cmd.target_bin, "config");
    var db = FlxPkgConfig::FlxPkgConfigQuery (list[string] pkgdir);
    gen getbootfields (field:string) => db.getpkgfield  ehandler (cmd.boot_package, field);
    var toolchain_plugins = getbootfields ("toolchain_plugin");
    var cygwin_toolchain_plugins = getbootfields ("cygwin_toolchain_plugin");
    var web_plugins = getbootfields ("web_plugin");
    var flx_tools = getbootfields ("flx_tool");
    var tools = getbootfields ("tool");

    // at this point, the build proceeds using host tools, but only target sources.
    if PLAT_CYGWIN do // requires cygwin dll and headers so only on Cygwin!
      if cmd.build_toolchain_plugins call
        build_plugins(cmd.target_dir, cmd.target_bin,
        toolchain_plugins+cygwin_toolchain_plugins+"flx_plugin")
      ;
      if cmd.build_flx call
        build_flx(cmd.target_dir, cmd.target_bin, toolchain_plugins+cygwin_toolchain_plugins)
      ;
    else
      if cmd.build_toolchain_plugins call
        build_plugins(cmd.target_dir, cmd.target_bin, toolchain_plugins+"flx_plugin")
      ;
      if cmd.build_flx call
        build_flx(cmd.target_dir, cmd.target_bin, toolchain_plugins)
      ;
    done

    if cmd.build_flx_tools call build_flx_tools(cmd.target_dir, cmd.target_bin, flx_tools);
    if cmd.build_web_plugins call build_plugins(cmd.target_dir, cmd.target_bin, web_plugins);
    if cmd.build_tools call build_exes(cmd.target_dir, cmd.target_bin, tools);
    if cmd.build_flx_web call build_flx_web (cmd.target_dir, cmd.target_bin, web_plugins);
    println$ "Build Complete";
  }

  proc print_help()
  {
    println$ "Usage: flx_build_boot ";
    println$ "";
    println$ "# locations";
    println$ "";
    println$ "  --pkg=bootpkg               default: build_boot";
    println$ "  --target-dir=target_dir     default: build/release";
    println$ "  --target-bin=target_bin     default: host";
    println$ "";
    println$ "";
    println$ "# compilation options";
    println$ "";
    println$ "  --build-toolchain-plugins   Felix compile the toolchain plugins";
    println$ "  --build-flx                 Felix compile flx";
    println$ "  --build-flx-tools           Felix compile flx build tools";
    println$ "  --build-web-plugins         Felix compile the webserver plugins";
    println$ "  --build-tools               Felix compile standard tools";
    println$ "  --build-flx-web             Felix compile web server executable";
    println$ "";
    println$ "  --debug                     do stuff verbosely";
    println$ "";
    println$ "# Environment variables";
    println$ "";
    println$ "FLX_SHELL_ECHO=1              echo all shell callouts (system, popen)";
    println$ "FLX_DEBUG_FLX=1               make 'flx' explain its processing decisions";
    println$ "";
    println$ "Purpose: Build new Felix target: stuff written in Felix";
    println$ "";
    println$ "Ensures target_dir contains:";
    println$ "";
    println$ "  (a) Repository source in $target_dir/share/src";
    println$ "  (b) Share library in $target_dir/share/lib";
    println$ "  (c) config db, C++ headers, libraries and executables in $target_dir/$target_bin/*";
    println$ "";
  }

  typedef cmd_type = typeof (parse_args Empty[string]);

  noinline fun parse_args (args: list[string]) =
  {
     var cmd = (
       boot_package="",
       target_dir="build"/"release",
       target_bin="host",

       build_web_plugins=false,
       build_toolchain_plugins=false,
       build_flx=false,
       build_flx_tools=false,
       build_tools=false,
       build_flx_web=false,
       debug = false
     );

     for arg in args do
       // location options
       if prefix(arg,"--pkg=") do
         &cmd.boot_package <- arg.[6 to];
       elif prefix(arg,"--target-dir=") do
         &cmd.target_dir <- arg.[13 to];
       elif prefix(arg,"--target-bin=") do
         &cmd.target_bin <- arg.[13 to];
       elif arg == "--debug" do
         &cmd.debug <- true;

       // operation options: compilation
       elif arg == "--build-web-plugins" do
         &cmd.build_web_plugins<- true;
       elif arg == "--build-toolchain-plugins" do
         &cmd.build_toolchain_plugins<- true;
       elif arg == "--build-flx" do
         &cmd.build_flx <- true;
       elif arg == "--build-flx-tools" do
         &cmd.build_flx_tools <- true;
       elif arg == "--build-tools" do
         &cmd.build_tools<- true;
       elif arg == "--build-flx-web" do
         &cmd.build_flx_web <- true;
       elif arg == "--build-all" do
         &cmd.build_web_plugins<- true;
         &cmd.build_toolchain_plugins<- true;
         &cmd.build_flx <- true;
         &cmd.build_flx_web <- true;
         &cmd.build_flx_tools <- true;
         &cmd.build_tools<- true;
       elif arg == "--help" do
         print_help();
         System::exit(0);
       else
         println$ "Unknown switch " + arg;
         print_help();
         System::exit(1);
       done
     done

     // Note: unrelated to boot package used by flx_build_rtl
     if cmd.boot_package == "" do &cmd.boot_package <- "build_boot"; done
     return cmd;
  }

  noinline proc build_felix (xargs:list[string])
  {
    if xargs.len.int < 2 do
      print_help();
      System::exit(1);
    done
    var cmd = parse_args (tail xargs);
    println$ "flx_build_boot v1.3";
    println$ "  build_package = " + cmd.boot_package;
    println$ "  target_dir    = " + cmd.target_dir;
    println$ "  target_bin    = " + cmd.target_bin;

    flx_build (cmd);
  }

}

Flx_client::setup;
FlxCoreBuild::build_felix (#System::args);

System::exit (0);