#line 19 "/home/travis/build/felix-lang/felix/src/packages/buildtools.fdoc"
  class BuildFlxg
  {
  
  /* HEY, this is FELIX! We have our own time services
  
    // this utter rubbish is required just to format the time.
    // Shame on the idiot C language, and even more on Posix!
  
    // some kind of integer time in seconds
    private type time_t = "time_t" requires Posix_headers::sys_time_h;
    private fun _ctor_long : time_t -> long = "(long)$1";
  
    // convert double to integer
    private fun _ctor_time_t : double -> time_t = "static_cast<time_t>($1)";
  
    // split up time structure
    private type struct_tm = "struct tm" requires C89_headers::time_h;
  
    // convert integer time to split up time
    private proc gmtime_r : &time_t * &struct_tm;
  
    // format split up time
    private gen strftime: +char * size * +char * &struct_tm -> size;
  
    private gen fmt_time (t: double, fmt:string) = {
       var  tt = time_t t; // double to time_t
       var tm:struct_tm;
       gmtime_r(&tt,&tm); // split up time_t into pieces
       var buffer = varray[char] (100uz, char 0);
       var n = strftime(buffer.stl_begin,buffer.len, fmt.cstr, &tm);
       return string(buffer.stl_begin, n);
    }
  */
  
    // 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 = 'tmp-dir';
      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 = 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;
      }
  
      union build_kind = Library | Executable;
  
      gen ocaml_build(kind:build_kind, dir:string, lib:string, spec:libspec_t) =
      {
  
        println$ "-" * 20;
        println$ "Lib=" + lib + " in " + dir;
        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 = 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 +
                 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 +
                 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") +
              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 +
              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 map (fun (f:string) = { return d/f;}) files;
      }
  
      gen mls_nodyp (d:string) = {
        var files = FileSystem::regfilesin (d, RE2 '.*\\.(mli?|mll|mly)');
        return 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]('nums', '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]('nums')) 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]('nums', '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]('nums')) 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]('nums')) 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('nums.cmxa', '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();