Package: src/packages/flx.fdoc
Flx compiler driver tool¶
key | file |
---|---|
bootflx_tool.flx | $PWD/src/tools/bootflx.flx |
flx_tool.flx | $PWD/src/tools/flx.flx |
dflx_tool.flx | $PWD/src/tools/dflx.flx |
key | file |
---|---|
flx_control.flx | share/lib/std/felix/flx/flx_control.flx |
flx_cmdopt.flx | share/lib/std/felix/flx/flx_cmdopt.flx |
flx_depvars.flx | share/lib/std/felix/flx/flx_depvars.flx |
flx_plugin_client.flx | share/lib/std/felix/flx/flx_plugin_client.flx |
flx_run.flx | share/lib/std/felix/flx/flx_run.flx |
flx.flx | share/lib/std/felix/flx/flx.flx |
bootflx.flx | share/lib/std/felix/flx/bootflx.flx |
Felix flx
tool.¶
This is exactly the same as the dflx tool, it just runs the @[@f@l@x@r@u@n@}@ @l@i@b@r@a@r@y@ @f@u@n@c@t@i@o@n@ @w@i@t@h@ @c@o@m@m@a@n@d@ @l@i@n@e@ @a@r@g@u@m@e@n@t@s@. However it preloads some plugins that might be used to avoid run time loading.
//[flx_tool.flx]
// flx plugin linker
//
class FlxPluginSymbols
{
// We have to do this dummy requirements because static
// linking removes
requires package "re2";
requires package "faio";
requires package "flx_arun";
open Dynlink;
// Now add all the symbols.
proc addsymbols ()
{
static-link-plugin
toolchain_clang_macosx,
toolchain_iphoneos,
toolchain_iphonesimulator,
toolchain_clang_linux,
toolchain_gcc_macosx,
toolchain_gcc_linux,
toolchain_msvc_win
;
// flx
static-link-symbol dflx_create_thread_frame in plugin dflx;
static-link-symbol dflx_flx_start in plugin dflx;
}
}
// Add the symbols
FlxPluginSymbols::addsymbols;
// Now invoke the program!
val linstance = Dynlink::prepare_lib("dflx");
var init: cont = Dynlink::get_init linstance;
Fibres::chain init;
Command line tool dflx.¶
This tool just runs the runflx
library function
with the executable command line arguments.
//[dflx_tool.flx]
include "std/felix/flx/flx";
System::pexit$ Flx::runflx #System::args;
Control Record.¶
Just initialises the base configuration data.
//[flx_control.flx]
class FlxControl
{
proc print_options(control:control_type) {
println$ "NOOPTIMISE = "+str control.NOOPTIMISE;
println$ "STATIC = "+str control.STATIC;
println$ "ECHO = "+str control.ECHO;
println$ "NOSTDLIB = "+str control.NOSTDLIB;
println$ "DEBUG = "+str control.DEBUG;
println$ "DEBUG_COMPILER = "+str control.DEBUG_COMPILER;
println$ "STDIMPORTS = "+str control.STDIMPORTS;
println$ "STDGRAMMAR = "+str control.STDGRAMMAR;
println$ "IMPORTS = "+str control.IMPORTS;
println$ "RECOMPILE = "+str control.RECOMPILE;
println$ "FLXG_FORCE = "+str control.FLXG_FORCE;
println$ "ocamls = "+str control.ocamls;
println$ "cpps = "+str control.cpps;
println$ "cppos = "+str control.cppos;
println$ "TIME = "+str control.TIME;
println$ "COMPILER_TIME = "+str control.COMPILER_TIME;
println$ "BUNDLE_DIR = "+str control.BUNDLE_DIR;
println$ "RUNIT = "+str control.RUNIT;
println$ "CCOMPILEIT = "+str control.CCOMPILEIT;
println$ "LINKIT = "+str control.LINKIT;
println$ "RUNONLY = "+str control.RUNONLY;
println$ "CXXONLY = "+str control.CXXONLY;
println$ "OCAMLONLY = "+str control.OCAMLONLY;
println$ "FELIX = "+str control.FELIX;
println$ "LINKER_SWITCHES = "+str control.LINKER_SWITCHES;
println$ "LINKER_OUTPUT_FILENAME = "+str control.LINKER_OUTPUT_FILENAME;
println$ "FLX_INTERFACE_FILENAME = "+str control.FLX_INTERFACE_FILENAME;
println$ "CXX_INTERFACE_FILENAME = "+str control.CXX_INTERFACE_FILENAME;
println$ "MACROS = "+str control.MACROS;
println$ "SHOWCODE = "+str control.SHOWCODE;
println$ "USAGE = "+control.USAGE;
println$ "DOREDUCE = "+str control.DOREDUCE;
println$ "OPTIMISE = "+str control.OPTIMISE;
}
fun init_loopctl () => struct {
// Argument parsing loop
var argno=1;
var grab=1;
var path="";
var ext="";
var base="";
var dir="";
var progname = "";
};
typedef loopctl_type = typeof (#init_loopctl);
fun dflt_control () =>
struct {
var PRINT_HELP=0;
var FLXG_FORCE=0;
var RECOMPILE=0;
var RUNIT=1;
var CCOMPILEIT=1;
var LINKIT=1;
var LINKEXE=0; // default is to link a DLL
var FELIX=1;
var RUNONLY=0;
var CXXONLY=0;
var OCAMLONLY=0;
var ECHO=0;
var DEBUG_FLX=false;
var VALIDATE_CACHE=1;
var CHECK_DEPENDENCIES=1;
var FLX_TOOLCHAIN="";
var FLX_TARGET_SUBDIR="";
// --------------------------------------------------
// processing options
// --------------------------------------------------
var DIST_ROOT="";
var DEBUG=0;
var DEBUG_COMPILER=0;
var COMPILER_PHASE="";
var INLINE=25;
var COMPILER_TIME=0;
var TIME=0;
var NOOPTIMISE=0;
var DOREDUCE=1;
var TIMECMD="time -p";
var STATIC=0;
var STATICLIB=0;
var SHOWCODE=0;
var CCFLAGS=Empty[string];
var EXTRA_CCFLAGS=Empty[string];
var EXTRA_PACKAGES=Empty[string];
var LINKER_SWITCHES=Empty[string];
var MACROS=Empty[string];
var cpps=Empty[string];
var cppos=Empty[string];
var ocamls=Empty[string];
var STANDARD_INCLUDE_FILES=Empty[string];
var EXTRA_INCLUDE_DIRS=Empty[string];
var EXTRA_INCLUDE_FILES=Empty[string];
var FLX_STD_LIBS=Empty[string];
var NOSTDLIB=0;
var STDOUT="";
var EXPECT="";
var CHECK_EXPECT=0;
var SET_STDIN=0;
var STDIN="";
var GRAMMAR_DIR="";
var STDGRAMMAR="";
//var STDIMPORTS = Cons ("plat/flx.flxh", Cons ( "concordance/concordance.flxh", Empty[string]));
var STDIMPORTS = (["plat/flx.flxh", "concordance/concordance.flxh"]);
var CMDLINE_INPUT=false;
var REPL_MODE=false;
var AUTOMATON="";
var IMPORTS=Empty[string];
var USAGE = "production";
var CLEAR_CACHE=0;
var BUNDLE_DIR = match Env::getenv("FLX_BUNDLE_DIR") with | "" => None[string] | dir => Some dir endmatch;
var DRIVER_EXE = ""; // dynamic linkage only
var DRIVER_OBJS = Empty[string]; // static linkage only
var LINK_STRINGS = Empty[string];
var pkgs=Empty[string];
var extra_pkgs = Empty[string];
var FLXG = "";
var FLXRUN = Empty[string];
var LINKER_OUTPUT_FILENAME = "";
var FLX_INTERFACE_FILENAME = "";
var CXX_INTERFACE_FILENAME = "";
var OUTPUT_FILENAME_SPECIFIED = 0;
var OUTPUT_FILENAME_WITHOUT_EXTENSION_SPECIFIED = 0;
var OUTPUT_DIRECTORY_SPECIFIED = 0;
var USER_ARGS = Empty[string];
var DLINK_STRINGS = Empty[string];
var SLINK_STRINGS = Empty[string];
var cache_time = 0.0;
var INDIR = "";
var INREGEX = "";
var NONSTOP = 0;
var OPTIMISE = list[string]$ "-O1";
var FLXG_OPTIMISE= 0;
}
;
typedef control_type = typeof (#dflt_control);
}
Command line argument parser.¶
Parses the command line options.
//[flx_cmdopt.flx]
// NOTE: below the string "host" is used to help find files eg flxg.
// This is a temporary hack to get Felix working after filesystem reorgnisation.
class FlxCmdOpt
{
private proc print_help() {
println "Usage: flx [options] filename[.flx] [args ..]";
println "options:";
println "--cmd=text : save text to file 'cmd.flx' and process that";
println "--repl : enter REPL mode saving stuff in session.flx and library.flx";
println "--test : use felix installation in current directory";
println "--test=dir : use felix installation in dir";
println "--target=dir : subdir of install dir containing target configuration (default 'host')";
println "--target-dir=dir : dir containing target configuration (default '$FLX_INSTALL_DIR/host')";
println "--pkgconfig-path+=dir: prepend extra flx_pkgconfig search directory to standard path";
println "--toolchain=toolchain: pick a non-default C++ compiler toolchain";
println "--felix=file : get installation details from file";
println "--where : print location of felix installation";
println "--show : print the felix program to stdout";
println "-c : compile only, do not run";
println "-o : linker output filename";
println "-ox : linker output filename (without extension)";
println "-od : linker output directory" ;
println "--usage=prototype : fast compilation at the expense of slower executables";
println "--usage=debugging : enable debugging aids";
println "--usage=production : optimised code with run time safety checks retained";
println "--usage=hyperlight : optimised code without run time safety checks";
println "--static : make standalone statically linked executable";
println "--staticlib : make standalone library of static objects";
println "--nofelix : do not run felix translator, leave C++ outputs alone";
println "--nocc : do not C/C++ compiler; implies --nolink";
println "--nolink : do not link object files to an executable";
println "--exe : link executable";
println "--run-only : run program without dependency checking or linking";
println "--c++ : Pure C++ build, no Felix code";
println "--ocaml : Pure Ocaml build, no Felix code";
println "--options : show option set";
println "--config : show configuration";
println "--version : show felix version";
println "--force : force run Felix compiler";
println "--force-compiler : force Felix compiler to rebuild everything";
println "--cache-dir=dir : directory cache output from parser (*.par files), autocreated, default $HOME/.felix/cache";
println "--output-dir=dir : directory to hold C++ output from translator, autocreated, default $HOME/.felix/cache";
println " Felix stored by absolute pathname within directory (tree directory).";
println "--bundle-dir=dir : directory to hold C++ output from translator, autocreated.";
println " Files directly in directory by basename (flat directory).";
println "--clean : delete the caches first";
println "--help : show this help";
println "--noinline : force inlining off, may break things!";
println "--inline : aggressive inlining";
println "--inline=999 : set inline cap to 999 'instructions'";
println "--echo : print shell commands before running them";
println "--time : print target program run time after it finishes";
println "--compile-time : print time for compiler phases";
println "--nostdlib : don't load the standard library";
println "--nooptimise : disable C++ compiler optimisation";
println "--noreduce : disable reductions (default for compilation speed)";
println "--doreduce : enable reductions (default for performance)";
println "--debug : put debug symbols in generated binaries";
println "--debug-compiler : make felix compiler print progress diagnostics";
println "--debug-flx : make flx tool print diagnostics";
println "--stdout=file : run program with standard output redirected to file";
println "--expect=file : compare stdout with expect file";
println "--expect : compare stdout with basename.expect";
println "--input=file : set standard input";
println "--input : set standard input to basename.input";
println "--indir=dir : set directory for regexp search, default current directory";
println "--regex=pattern : Perl regexp for batch file processing";
println "--nonstop : don't stop on error in batch processing";
println "--backup : backup working source tree to dir 'backup'";
println "--import=file : add an import which is prefixed to all files being translated";
println "--import=@file : add all the files listed in file as imports (recursive on @)";
println "--nostdimport : don't import the standard imports nugram.flxh and flx.flxh";
println "--compiler-phase : specify which phase of the compiler to run";
println "-Idir : add dir to search path for both felix and C++ includes";
println "-Ldir : add dir to linker search path";
println "-llib : add dir lib to linker command";
println "-foption : add switch to compiler command";
println "-Woption : add switch to compiler command";
println "-O0 : add switch to compiler command";
println "-O1 : add switch to compiler command";
println "-O2 : add switch to compiler command";
println "-O3 : add switch to compiler command";
println "--cflags=flags : addd flags to compiler command";
println "-Dmac : add macro def to C++ compiler command";
println "-DFLX_ENABLE_TRACE : enable compilation of trace generators (defaults off)";
println "-DFLX_CGOTO : use gcc indirect gotos and use assembler hack for long jumps (default on if config detects support)";
println "";
println "*.c *.cc *.cpp *.cxx ";
println " : add files to C++ compilation (and linker) steps";
println "*.o *.obj *.lib *.dll *.a *.so";
println " : add files to linker steps";
println "* *.flx *.fdoc : Felix program name, terminates options and starts runtime arguments";
println "";
println "Environment variables";
println "---------------------";
println "Flx build tool";
println " FLX_INSTALL_DIR=dir : overrides default installation directory (as if --test=dir)";
println " FLX_SHELL_ECHO=1 : show shell callouts (system,popen)";
println " FLX_FILE_MONITOR=1 : reports on every file open (felix and flxg)";
println " FLX_REPORT_FILECOPY=1 : reports on every file copy (felix)";
println " FLX_DEBUG_FLX=1 : debug flx (as if --debug-flx set)";
println "";
println "Flxg compiler";
println " FLX_DEBUG_PARSER=1 : emit debug info from the Felix parser";
println " FLX_DEBUG_COMPILER_UNIQ=1 : emit debug of uniq flow analyser, instruction and flow analysis";
println " FLX_DEBUG_COMPILER_UNIQ_GETSET=1 : emit debug of uniq flow analyser, instruction analysis";
println "";
println "Run time system (affects flx as well as any binary run)";
println " FLX_DEBUG : enable debugging traces (default off)";
println " FLX_DEBUG_ALLOCATIONS : enable debugging allocator (default FLX_DEBUG)";
println " FLX_DEBUG_COLLECTIONS : enable debugging collector (default FLX_DEBUG)";
println " FLX_REPORT_COLLECTIONS : report collections (default FLX_DEBUG)";
println " FLX_DEBUG_THREADS : enable debugging collector (default FLX_DEBUG)";
println " FLX_DEBUG_DRIVER : enable debugging driver (default FLX_DEBUG)";
println "";
println "Run time GC tuning (affects flx as well as any binary run)";
println " FLX_FINALISE : whether to cleanup on termination (default NO)";
println " FLX_GC_FREQ=n : how often to call garbage collector (default 1000)";
println " FLX_MIN_MEM=n : initial memory pool n Meg (default 10)";
println " FLX_MAX_MEM=n : maximum memory n Meg (default -1 = infinite)";
println " FLX_FREE_FACTOR=n.m : reset FLX_MIN_MEM to actual usage by n.m after gc (default 1.1)";
println " FLX_ALLOW_COLLECTION_ANYWHERE # (default yes)";
println "";
println "Felix Developer debugging";
println " FLX_DEBUG_USTR=1 : # Show malloc/realloc/free in ustr (default no)";
}
// TODO: change the names of everything to match exactly the command line
// switches so this can be used as a response file
proc setup-from-file (debugln: string -> 0)
(
config:&Config::config_type,
control:&FlxControl::control_type,
arg:string
)
{
debugln$ "Setup file: " + arg;
var text = load arg;
Config::process_config_text config (text);
debugln$ "Config[after setupfile "+arg+"] =\n" + str (*config);
control <- FlxControl::dflt_control();
if control*.DEBUG_FLX call FlxControl::print_options(*control);
fun / (a:string, b:string) => Filename::join (a,b);
var re = RE2 ("([-_a-zA-Z0-9]+) *: *(.*)");
var lines = split (load arg,char "\n");
for line in lines do
match Match (re,line) with
| Some v =>
var field = v.1;
var data = strip v.2;
match field with
| "felix-compiler" => debugln$ "set flxg " + data; control.FLXG <-data;
| "toolchain" => debugln$ "set toolchain "+data; control.FLX_TOOLCHAIN <- data;
| "linker-switch" => debugln$ "add linker switch "+data;
control.LINKER_SWITCHES <- control*.LINKER_SWITCHES + data;
| "macro-switch" => debugln$ "add macro switches "+data;
control.MACROS <- control*.MACROS + data;
| "optimisation-switch" => debugln$ "set C++ optimisation level "+data;
control.OPTIMISE <- control*.OPTIMISE + data;
// American spelling
| "optimization-switch" => debugln$ "set C++ optimization level "+data;
control.OPTIMISE <- control*.OPTIMISE + data;
| "cflag" => debugln$ "add C++ cflag "+data;
control.EXTRA_CCFLAGS <- control*.EXTRA_CCFLAGS + data;
| "flx-include-dir" => debugln$ "add Felix include dir "+data;
config.FLX_LIB_DIRS <- config*.FLX_LIB_DIRS + data;
| "rtl-include-dir" => debugln$ "add Felix and C++ rtl include dir "+data;
config.FLX_RTL_DIRS <- config*.FLX_RTL_DIRS + data;
| "grammar-dir" => debugln$ "set Felix grammar directory "+data;
control.GRAMMAR_DIR <- data;
| "grammar" => debugln$ "set Felix grammar (in stdlib) "+data;
control.STDGRAMMAR <- data;
| "std-import" => debugln$ "set Felix standard import (in stdlib) "+data;
control.STDIMPORTS <- data ! control*.STDIMPORTS;
| "extra-import" => debugln$ "set Felix extra import (in stdlib) "+data;
control.IMPORTS <- control*.IMPORTS + data;
| "extra-cpp" => debugln$ "set Felix extra C++ file "+data;
control.cpps <- control*.cpps + data;
| "extra-obj" => debugln$ "set Felix extra object file "+data;
control.cppos <- control*.cppos + data;
| "flx-std-lib" => debugln$ "add Felix standard (cached) library "+data;
control.FLX_STD_LIBS <- control*.FLX_STD_LIBS+ data;
| _ => debugln$ "Unknown field " + field;
endmatch;
| #None => ;
endmatch;
done
}
private noinline proc handle_switch
(
config:&Config::config_type,
control:&FlxControl::control_type,
arg:string
)
{
proc debugln[T with Str[T]] (x:T) {
if control*.DEBUG_FLX call fprintln (cstderr, "[flx] " + str x);
}
if prefix(arg,"--cmd=") do
begin
var text = arg.[6 to];
save( "cmd.flx", text+";\n");
control.CMDLINE_INPUT <- true;
debugln("Running command '" + text + ";'");
end
elif arg == "--repl" do
control.REPL_MODE <- true;
debugln("Set REPL mode");
elif arg == "--nostdimport" do
debugln "No standard library import";
// Note: currently, Felix compiler generates code that REQUIRES
// the standard library, eg the driver passes a gc_profile_t record
// and the compiler generates _uctor_ objects, etc etc
control.STDIMPORTS <- list[string]();
elif prefix(arg,"--import=") do
debugln "Add import";
control.IMPORTS <- control*.IMPORTS + arg.[9 to];
elif prefix(arg,"--felix=") do
debugln "Set install details";
setup-from-file debugln[string] (config, control, arg.[8 to]);
elif prefix(arg,"--target=") do
begin
debugln "Set target subdirectory";
var a = arg.[9 to];
control.FLX_TARGET_SUBDIR <- a;
//println$ "SET FLX_TARGET_SUBDIR TO " + control*.FLX_TARGET_SUBDIR;
//println$ "Current FLX_INSTALL_DIR IS " + config*.FLX_INSTALL_DIR;
Config::cascade_FLX_TARGET_DIR config (Filename::join (config*.FLX_INSTALL_DIR, control*.FLX_TARGET_SUBDIR));
//println$ "SET FLX_TARGET_DIR TO " + config*.FLX_TARGET_DIR;
end
elif prefix(arg,"--target-dir=") do
debugln "Set target configuration directory";
Config::cascade_FLX_TARGET_DIR config arg.[13 to];
elif prefix(arg,"--pkgconfig-path+=") do
debugln "Prepend extra flx_pkgconfig directory to standard path";
config.FLX_CONFIG_DIRS <- arg.[18 to] + config*.FLX_CONFIG_DIRS;
elif prefix(arg,"--toolchain=") do
debugln "Set toolchain";
control.FLX_TOOLCHAIN<- arg.[12 to];
elif prefix(arg,"--test=") do
var a = arg.[7 to];
debugln "Set test directory";
Config::cascade_FLX_INSTALL_DIR config a;
control.FLX_TARGET_SUBDIR <- "host";
elif arg=="--test" do
begin
debugln "Set test directory";
a = ".";
Config::cascade_FLX_INSTALL_DIR config a;
control.FLX_TARGET_SUBDIR <- "host";
end
elif prefix(arg,"--stdout=") do
debugln "Redirect standard output";
// of the Felix program only: used for saving the output
// to a file so the test harness can compare it with an .expect file
control.STDOUT <- arg.[9 to];
elif arg == "--expect" do
debugln "compare stdout with expect file (default name)";
// of the Felix program only: used for saving the output
// to a file so the test harness can compare it with an .expect file
control.CHECK_EXPECT <- 1;
elif prefix(arg,"--expect=") do
debugln "compare stdout with expect file";
// of the Felix program only: used for saving the output
// to a file so the test harness can compare it with an .expect file
control.EXPECT <- arg.[9 to];
control.CHECK_EXPECT <- 1;
elif arg == "--input" do
debugln "redirect stdin to (default name)";
control.SET_STDIN <- 1;
elif prefix(arg,"--input=") do
debugln "redirect stdin to file";
control.STDIN <- arg.[8 to];
control.SET_STDIN <- 1;
elif arg=="--show" do
control.SHOWCODE <- 1;
elif arg=="--clean" do
debugln "Clear caches";
control.CLEAR_CACHE <- 1;
elif arg=="--force" do
debugln "Force recompilation";
// of the felix code, runs Felix unless --nofelix is set
// the C++ compiler is run unless the felix compile failed
control.RECOMPILE <- 1;
elif arg=="--force-compiler" do
debugln "Force flxg compiler to rebuild everything";
// of the felix code, runs Felix unless --nofelix is set
// the C++ compiler is run unless the felix compile failed
control.RECOMPILE <- 1;
control.FLXG_FORCE<- 1;
elif arg=="--debug-flx" do
control.DEBUG_FLX <- true;
control.ECHO <- 1;
debugln "debug flx tool ON";
control.DEBUG <- 1;
elif arg=="--debug" do
debugln "Enable runtime debugging";
control.DEBUG <- 1;
elif arg=="--debug-compiler" do
debugln "Enable compiler debugging";
control.DEBUG_COMPILER <- 1;
elif prefix(arg,"--compiler-phase=") do
debugln "Change the compiler phase";
control.COMPILER_PHASE <- arg.[len "--compiler-phase=" to];
control.RUNIT <- 0;
elif arg=="--nooptimise" do
debugln "Disable optimisation";
control.NOOPTIMISE <- 1;
control.DOREDUCE <- 0;
elif arg in ("--compiler-optimise","--compiler-optimize") do
debugln "Enable heavy flxg optimisation";
control.FLXG_OPTIMISE <- 1;
elif arg=="--nostdlib" do
debugln "Do not load standard library";
control.NOSTDLIB <- 1;
elif arg == "--echo" do
debugln "Echo commands sent to system";
control.ECHO <- 1;
elif arg == "--noreduce" do
debugln "do not perform reductions";
control.DOREDUCE <- 0;
elif arg == "--doreduce" do
debugln "do perform reductions";
control.DOREDUCE <- 1;
elif arg == "--static" do
debugln "Compile a statically linked program";
control.STATIC <- 1;
control.LINKEXE<- 1;
elif arg == "--staticlib" do
debugln "make a static link library (instead of a program)";
control.STATIC <- 1;
control.STATICLIB <- 1;
control.RUNIT <- 0;
control.LINKEXE<- 0;
elif arg == "--exe" do
debugln "make an executable";
control.LINKEXE<- 1;
elif prefix(arg,"--inline=") do
debugln "Set inline aggressiveness";
control.INLINE <- int(arg.[9 to]);
elif arg == "--inline" do
debugln "Set inline aggressiveness";
control.INLINE <- 100;
elif arg == "--noinline" do
debugln "Disable inlining (NOT RECOMMENDED)";
control.INLINE <- 0;
elif arg == "--version" do
debugln "Print Felix version and exit";
print("version ");
println(Version::felix_version);
System::exit(0);
elif arg == "--config" do
println (*config);
System::exit(0);
elif arg == "--options" do
FlxControl::print_options(*control);
System::exit(0);
elif arg == "--where" do
debugln "Print location of install directory and exit";
println(config*.FLX_INSTALL_DIR);
System::exit(0);
elif arg == "--time" do
debugln "Time program execution and print after running";
control.TIME <- 1;
elif arg == "--compile-time" do
debugln "Print time of Felix compiler phases";
control.COMPILER_TIME <- 1;
elif prefix(arg,"--output_dir=") or prefix(arg,"--output-dir=") do
debugln "Set the directory for compiler generated C++ files";
config.FLX_OUTPUT_DIR <- arg.[13 to];
elif prefix(arg,"--bundle_dir=") or prefix(arg,"--bundle-dir=") do
debugln "Output files needed for C++ compilation into this folder (directly by basename)";
control.BUNDLE_DIR <- Some arg.[13 to];
elif prefix(arg,"--cache_dir=") or prefix(arg,"--cache-dir=") do
debugln "Set the directory for compiler generated *.par files";
config.FLX_CACHE_DIR <- arg.[12 to];
elif arg == "--usage=prototype" do
debugln "Set usage prototyping";
control.USAGE <- "prototype";
control.NOOPTIMISE <- 1;
control.OPTIMISE <- list[string]$ "-O1";
control.DOREDUCE <- 0;
control.INLINE <- 5;
elif arg in ("--usage=debugging","--usage=debug") do
debugln "Set usage debugging";
control.USAGE <- "debugging";
control.NOOPTIMISE <- 1;
control.DEBUG <- 1;
control.DOREDUCE <- 0;
control.OPTIMISE <- list[string]$"-O0";
control.INLINE <- 5;
elif arg == "--usage=production" do
debugln "Set usage production";
control.USAGE <- "production";
control.DOREDUCE <- 1;
control.OPTIMISE <- list[string]$"-O2";
control.INLINE <- 25;
control.FLXG_OPTIMISE <- 1;
elif arg == "--usage=hyperlight" do
debugln "Set usage hyperlight";
control.USAGE <- "hyperlight";
control.DOREDUCE <- 1;
control.OPTIMISE <- list[string]$"-O2";
control.INLINE <- 100;
control.FLXG_OPTIMISE <- 1;
elif arg == "--help" do
control.PRINT_HELP <- 1;
elif arg == "-c" do
debugln "Compile program but do not run it";
control.RUNIT <- 0;
elif prefix(arg,"-I") do
debugln "Set include directories for both Felix and C/C++";
config.FLX_LIB_DIRS<- config*.FLX_LIB_DIRS + arg.[2 to];
config.FLX_RTL_DIRS<- config*.FLX_RTL_DIRS + arg.[2 to];
elif arg== "--nofelix" do
debugln "Do not translate Felix code, just compile generated C++ (used to debug at C++ level)";
control.FELIX <- 0;
elif arg== "--nocc" do
debugln "Do not run the C/C++ compiler, just generate C++ source code and exit; implies -c and --nolink";
control.CCOMPILEIT <- 0;
elif arg== "--nolink" do
debugln "Do not link object code to an executable, just generate and compile the C++ source code; implies -c";
control.LINKIT <- 0;
elif arg == "--run-only" do
debugln "Run the binary executable without any compilation. Must exist!";
control.FELIX <-0;
control.CCOMPILEIT <- 0;
control.LINKIT <- 0;
control.LINKEXE <- 0;
control.RUNIT <- 1;
control.VALIDATE_CACHE <- 0;
control.CHECK_DEPENDENCIES <- 0;
control.RUNONLY <- 1;
elif prefix(arg,"-l") or prefix(arg,"-L") do
debugln "Set extra switched for linker";
control.LINKER_SWITCHES <- control*.LINKER_SWITCHES + arg;
elif prefix(arg,"-D") do
debugln "Set extra macros for C++ compilation";
control.MACROS <- control*.MACROS + arg;
elif arg \in ("-O0", "-O1","-O2","-O3") do
debugln$ "Set C++ compilation optimisation " + arg;
control.OPTIMISE <- list[string]$ arg;
elif prefix(arg,"-f") do
debugln$ "Set C++ compilation switch "+arg;
control.EXTRA_CCFLAGS <- control*.EXTRA_CCFLAGS + arg;
elif prefix(arg,"--cflags=") do
{
var flags = arg.[9 to];
debugln$ "Set C++ compilation switch "+ flags;
control.EXTRA_CCFLAGS <- control*.EXTRA_CCFLAGS + flags;
};
elif prefix(arg,"-W") do
debugln$ "Set C++ warning switch "+arg;
control.EXTRA_CCFLAGS <- control*.EXTRA_CCFLAGS + arg;
elif prefix(arg,"--pkg=") do
debugln "Add pkgconfig package to link";
control.pkgs <- control*.pkgs +arg.[6 to];
elif prefix (arg,"--indir=") do
control.INDIR <- arg.[8 to];
debugln$ "Set input directory for regexp to " + control*.INDIR;
elif prefix (arg,"--regex=") do
control.INREGEX <- arg.[8 to];
debugln$ "Set input regex to " + control*.INREGEX;
elif arg == "--nonstop" do
control.NONSTOP <- 1;
debugln$ "Set batch processing mode to nonstop " + control*.NONSTOP;
elif arg == "--c++" do
control.CXXONLY <- 1;
control.FELIX <- 0;
debugln$ "C++ only, no Felix";
elif arg == "--ocaml" do
control.OCAMLONLY <- 1;
control.FELIX <- 0;
debugln$ "Ocaml only, no Felix";
// the main filename -- subsequent args are args to flx_run
else
eprintln$ "Unknown switch '" + arg+"'";
System::exit 1;
done
}
private noinline proc handle_filename
(
ploopctl:&FlxControl::loopctl_type,
config:&Config::config_type,
control:&FlxControl::control_type,
arg:string
)
{
proc debugln[T with Str[T]] (x:T) {
if control*.DEBUG_FLX call fprintln (cstderr, "[flx] " + str x);
}
ploopctl.progname <- arg;
var path,ext = Filename::split_extension(arg);
ploopctl.path <- path;
ploopctl.ext <- ext;
var dir,base = Filename::split1(ploopctl*.path);
ploopctl.dir <- dir;
ploopctl.base <- base;
match check_ext $ Filename::get_extension arg with
| "compile" =>
control.cpps <- control*.cpps + arg;
| "link" =>
control.cppos <- control*.cppos + arg;
| "felix" =>
ploopctl.grab <- 0;
| "none" =>
ploopctl.grab <- 0;
| "unknown" =>
eprintln$ "Unknown file extension in " + arg;
System::exit 1;
| "ocaml" =>
control.ocamls<- control*.ocamls + arg;
| _ => assert false;
endmatch
;
}
// --------------------------------------------------
// String Utilities
// --------------------------------------------------
// utility to classify extensions.
private fun exts () = {
var compile_exts = list ('.cpp','.cxx','.c','.cc');
var ocaml = list ('.mli','.ml','.cmi','cmx','.cmxa');
var link_exts = list ('.o','.obj','.lib','.dll','.a','.so','.dylib','.os');
var felix_exts = list (".flx",".fdoc");
var exts =
map (fun (s:string) => s,"ocaml") ocaml+
map (fun (s:string) => s,"compile") compile_exts +
map (fun (s:string) => s,"link") link_exts +
map (fun (s:string) => s,"felix") felix_exts +
("","none")
;
return exts;
}
private fun check_ext (s:string) => match find #exts s with
| Some tag => tag
| #None => "unknown"
;
private noinline proc xparse_cmd_line
(
config:&Config::config_type,
control:&FlxControl::control_type,
ploopctl:&FlxControl::loopctl_type,
vargs: varray[string]
)
{
proc debugln[T with Str[T]] (x:T) {
if control*.DEBUG_FLX call fprintln (cstderr, "[flx] " + str x);
}
var SET_LINKER_OUTPUT = false;
var SET_LINKER_OUTPUT_WITHOUT_EXTENSION = false;
var SET_LINKER_OUTPUT_DIRECTORY = false;
grabbing_args: while ploopctl*.grab == 1 and ploopctl*.argno < vargs.len.int do
var arg = vargs . (ploopctl*.argno);
debugln$ "ARGNO="+str(ploopctl*.argno)+", arg='"+arg+"'";
if SET_LINKER_OUTPUT do
control.LINKER_OUTPUT_FILENAME <- arg;
debugln$ "Set linker output file=" + control*.LINKER_OUTPUT_FILENAME;
SET_LINKER_OUTPUT = false;
control.OUTPUT_FILENAME_SPECIFIED <- 1;
elif SET_LINKER_OUTPUT_WITHOUT_EXTENSION do
control.LINKER_OUTPUT_FILENAME <- arg;
debugln$ "Set linker output file=" + control*.LINKER_OUTPUT_FILENAME;
SET_LINKER_OUTPUT_WITHOUT_EXTENSION = false;
control.OUTPUT_FILENAME_WITHOUT_EXTENSION_SPECIFIED <- 1;
elif SET_LINKER_OUTPUT_DIRECTORY do
control.LINKER_OUTPUT_FILENAME <- arg;
debugln$ "Set linker output directory =" + control*.LINKER_OUTPUT_FILENAME;
SET_LINKER_OUTPUT_DIRECTORY= false;
control.OUTPUT_DIRECTORY_SPECIFIED <- 1;
elif arg == "-o" do
debugln "Set linker output name (next arg)";
SET_LINKER_OUTPUT=true;
elif arg == "-ox" do
debugln "Set linker output name (without extension) (next arg) ";
SET_LINKER_OUTPUT_WITHOUT_EXTENSION=true;
elif arg == "-od" do
debugln "Set linker output directory (next arg) ";
SET_LINKER_OUTPUT_DIRECTORY=true;
elif arg == "--" do
ploopctl.grab <- 0;
elif not (prefix (arg,"-")) do
handle_filename(ploopctl,config,control,arg);
else
handle_switch(config,control,arg);
done
ploopctl.argno <- ploopctl*.argno + 1;
done
if control*.CMDLINE_INPUT or control*.REPL_MODE do
handle_filename(ploopctl,config,control,"cmd.flx");
done
}
noinline proc processing_stage1
(
config:&Config::config_type,
control:&FlxControl::control_type,
xloopctl:&FlxControl::loopctl_type,
vargs:varray[string]
)
{
fun / (x:string, y:string) => Filename::join (x,y);
proc debugln[T with Str[T]] (x:T) {
if control*.DEBUG_FLX call fprintln (cstderr, "[flx] " + str x);
}
// process environment variables
if Env::getenv "FLX_DEBUG_FLX" != "" do
control.DEBUG_FLX <- true;
control.ECHO <- 1;
debugln "debug flx tool ON";
control.DEBUG <- 1;
done
xparse_cmd_line(config,control,xloopctl, vargs);
if control*.PRINT_HELP == 1 do
print_help;
System::exit(0);
done
var xqt = dxqt (control*.ECHO==1 or control*.DEBUG_FLX);
if control*.LINKIT == 0 and control*.STATICLIB == 1 do
eprintln$ "Conflicting switches --nolink and --staticlib";
System::exit 1;
done
debugln$ xloopctl*.grab, xloopctl*.argno, System::argc;
// Primary filename established.
debugln "#--------";
debugln$ "DONE, option index = "+str(xloopctl*.argno);
debugln$ "path="+xloopctl*.path+": dir="+xloopctl*.dir+",base="+xloopctl*.base+", ext="+xloopctl*.ext;
debugln$ "cpps="+str control*.cpps;
debugln$ "cppos="+str control*.cppos;
debugln$ "ocamls="+str control*.ocamls;
// Grab program arguments.
while xloopctl*.argno < vargs.len.int do
control.USER_ARGS `(+=) vargs . (xloopctl*.argno);
pre_incr (xloopctl.argno);
done
debugln$ "USER_ARGS=" + str control*.USER_ARGS;
debugln$ "config=" + str (*config);
// Establish C++ optimisation switches.
if control*.NOOPTIMISE == 0 do
debugln "Set C++ compiler optimisation switches";
control.CCFLAGS <- control*.CCFLAGS+ control*.OPTIMISE;
else
debugln "What, no optimisation?";
done
// Note we have to do it this way so the -f switches turn
// off optimisations previously introduced (order matters)
control.CCFLAGS <- control*.CCFLAGS + control*.EXTRA_CCFLAGS;
debugln$ "CCFLAGS =" + str control*.CCFLAGS;
// Establish name of Felix compiler and run time library.
// The one in "host" is good enough for flxg, however the
// library location MUST be changed for cross compilation.
// FIXME!
var dflt_flxg = "";
var dflt_flx_run = Empty[string];
if PLAT_WIN32 do
dflt_flxg = Filename::join(config*.FLX_TARGET_DIR, 'bin', 'flxg.exe');
dflt_flx_run = list$ "set", "PATH="+(Directory::mk_absolute_filename config*.FLX_TARGET_DIR)+"\\lib\\rtl;"+"%PATH%&&";
else
dflt_flxg = config*.FLX_TARGET_DIR+"/bin/flxg";
// the mac uses DYLD_LIBRARY_PATH instead of LD_LIBRARY_PATH
if PLAT_MACOSX do
dflt_flx_run = list$ "env","DYLD_LIBRARY_PATH="+config*.FLX_TARGET_DIR+"/lib/rtl:$DYLD_LIBRARY_PATH";
elif PLAT_CYGWIN do
// hack: we need to set BOTH since PATH is used for load time dynamic linkage
// but LD_LIBRARY_PATH for run time (dlopen style) dynamic linkage
dflt_flx_run = list$ "env",
"LD_LIBRARY_PATH="+config*.FLX_TARGET_DIR+"/lib/rtl:$LD_LIBRARY_PATH",
"PATH="+config*.FLX_TARGET_DIR+"/lib/rtl:$PATH"
;
else
dflt_flx_run = list$ "env", "LD_LIBRARY_PATH="+config*.FLX_TARGET_DIR+"/lib/rtl:$LD_LIBRARY_PATH";
done
done
control.FLXG <-
match control*.FLXG with
| "" => dflt_flxg
| x => x
endmatch
;
debugln$ "FLXG = " + control*.FLXG;
control.FLXRUN <-
match control*.FLXRUN with
| #Empty => dflt_flx_run
| x => x
endmatch
;
debugln$ "FLXRUN = " + control*.FLXRUN;
// TEMPORARY HACK: use the right stuff from the felix.fpc file
// a bit later .. for now the OS selection macros will do ..
fun link_strings () = {
var DLINK_STRING = "";
var SLINK_STRING = "";
if PLAT_WIN32 do // MSVC
DLINK_STRING = "/LIBPATH:"+config*.FLX_TARGET_DIR+r"\lib\rtl";
SLINK_STRING = "/LIBPATH:"+config*.FLX_TARGET_DIR+r"\lib\rtl";
elif PLAT_CYGWIN do // gcc on Windows
//DLINK_STRING = "-L"+config*.FLX_TARGET_DIR+"/bin";
DLINK_STRING = "-L"+config*.FLX_TARGET_DIR+"/lib/rtl";
SLINK_STRING = "-L"+config*.FLX_TARGET_DIR+"/lib/rtl";
else // Unix: gcc or clang
DLINK_STRING = "-L"+config*.FLX_TARGET_DIR+"/lib/rtl";
SLINK_STRING = "-L"+config*.FLX_TARGET_DIR+"/lib/rtl";
done;
return DLINK_STRING, SLINK_STRING;
}
// Get linker names.
var d,s = link_strings();
control.DLINK_STRINGS <- Shell::parse d;
control.SLINK_STRINGS <- Shell::parse s;
fun mkrel (d:string, f:string) =>
if Filename::is_absolute_filename f then f else d / f endif
;
var dflt_grammar_dir = config*.FLX_SHARE_DIR/"lib";
control.GRAMMAR_DIR <-
match control*.GRAMMAR_DIR with
| "" => dflt_grammar_dir
| x => Directory::mk_absolute_filename x
endmatch
;
debugln$ "GRAMMAR_DIR = " + control*.GRAMMAR_DIR;
var dflt_grammar = Directory::mk_absolute_filename
(Filename::join (control*.GRAMMAR_DIR,"grammar/grammar.files"))
;
control.STDGRAMMAR <-
match control*.STDGRAMMAR with
| "" => dflt_grammar
| x =>
if Filename::is_absolute_filename x then x
else Filename::join (control*.GRAMMAR_DIR, x)
endmatch
;
debugln$ "STDGRAMMAR = " + control*.STDGRAMMAR;
var dflt_automaton =
cache_join
(
config*.FLX_CACHE_DIR,
Filename::join (control*.STDGRAMMAR, "syntax.automaton")
)
;
control.AUTOMATON <-
match control*.AUTOMATON with
| "" => dflt_automaton
| x => x
endmatch
;
debugln$ "AUTOMATON = " + control*.AUTOMATON;
// this hack forces a directory name, because executing "prog"
// can fail if the currect directory is not on the PATH,
// or worse, the wrong program can execute. The PATH is not
// searched if the filename includes a / somewhere so force one in.
// similarly for dynamic loaders looking for shared libraries
//
// It would probably be better to convert any relative filename
// to an absolute one, however this only makes sense on Unix
// since Windows has multiple "drives" it is much harder to
// do the conversion.
xloopctl.dir <-
if xloopctl*.dir != "" then xloopctl*.dir
else "."
endif
;
}
}
Calculate Dependent variables.¶
Computes all the detailed variables needed to run the various tools from a base configuration.
//[flx_depvars.flx]
include "std/felix/flx/flx_control";
class FlxDepvars
{
typedef dvars_type = (
filebase:string,
cpp_filebase:string,
args: list[string],
use_ext:string,
FLX_STD_LIBS: list[string],
GRAMMAR_DIR: string,
STDGRAMMAR: string,
AUTOMATON: string,
DEBUGSWITCH:list[string],
STATIC_ENV:list[string],
VERBOSE: list[string]
);
gen cal_depvars(
toolchain_maker: toolchain_config_t -> toolchain_t,
c_compiler_executable: string,
cxx_compiler_executable: string,
config:Config::config_type,
control:&FlxControl::control_type,
loopctl:FlxControl::loopctl_type)
: dvars_type
=
{
proc debugln[T with Str[T]] (x:T) {
if control*.DEBUG_FLX call fprintln (cstderr, "[flx] " + str x);
}
fun / (d:string, f:string) => Filename::join (d,f);
// case 1 of dflt
var dflt_toolchain_config = (
c_compiler_executable = c_compiler_executable,
cxx_compiler_executable = cxx_compiler_executable,
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]
);
var tc = toolchain_maker dflt_toolchain_config;
var EXT_LIB = #(tc.static_library_extension);
var EXT_SHLIB = #(tc.dynamic_library_extension);
var EXT_EXE = #(tc.executable_extension);
var EXT_STATIC_OBJ = #(tc.static_object_extension);
var EXT_SHARED_OBJ = #(tc.dynamic_object_extension);
var DEBUG_FLAGS = #(tc.debug_flags);
debugln$ "Felix package manager config directories are "+config.FLX_CONFIG_DIRS.str;
// make a list of any *.cpp files (or other g++ options ..)
debugln$ "FileDir= " + loopctl.dir;
var rel_filebase = if loopctl.dir == "." then loopctl.base else Filename::join(loopctl.dir,loopctl.base);
debugln$ "Rel_filebase= " + rel_filebase;
debugln$ "Given Extension=" + loopctl.ext;
// this is a hack! We should resolve the filename first.
var use_ext = if loopctl.ext != "" then loopctl.ext else
#{
var flxt = FileStat::dfiletime (rel_filebase+".flx",#FileStat::past_time);
var fdoct = FileStat::dfiletime (rel_filebase+".fdoc",#FileStat::past_time);
return
if flxt > fdoct then ".flx"
elif fdoct > flxt then ".fdoc"
else ""
;
}
;
debugln$ "Computed Extension=" + use_ext;
var filebase = Directory::mk_absolute_filename$ rel_filebase;
debugln$ "User program base is " + filebase;
var cpp_filebase =
match control*.BUNDLE_DIR with
| Some dir => Filename::join(dir,Filename::basename filebase)
| #None =>if config.FLX_OUTPUT_DIR=="" then filebase
else cache_join(config.FLX_OUTPUT_DIR,filebase)
endif
endmatch;
debugln$ "C++ file base is " + cpp_filebase;
// if we're supposed to check output against an expect file,
// and no stdout file name is given, then direct output
// into the cache.
if control*.CHECK_EXPECT != 0 and control*.STDOUT == "" do
control.STDOUT <- cache_join (config.FLX_OUTPUT_DIR,filebase + ".stdout");
debugln$ "Set stdout to " + control*.STDOUT;
done
if control*.SET_STDIN != 0 and control*.STDIN == "" do
var stdin_name = filebase + ".input";
if FileStat::fileexists stdin_name do
control.STDIN <- stdin_name;
elif control*.INREGEX == "" do
eprintln$ "WARNING: computed input file " + stdin_name + " doesn't exist!";
done
debugln$ "Set stdin to " + control*.STDIN;
done
// if we're supposed to check output against an expect file,
// and no expect file name is given, then use the filebase
// with extension .expect.
if control*.CHECK_EXPECT != 0 and control*.EXPECT == "" do
var expect_name = filebase + ".expect";
if FileStat::fileexists expect_name do
control.EXPECT <- expect_name;
elif control*.INREGEX == "" do
eprintln$ "WARNING: computed expect file " + expect_name + " doesn't exist!";
done
debugln$ "Set expect to " + control*.EXPECT;
done
// Find absolute pathname
if loopctl.path == "" do
fprint$ cstderr, ("No such felix program: "+loopctl.path+"\n");
System::exit(1);
done
control.FLX_INTERFACE_FILENAME <-
match control*.BUNDLE_DIR with
| Some dir => Filename::join(dir,Filename::basename filebase+"_interface.flx")
| #None => cache_join (config.FLX_OUTPUT_DIR,filebase+"_interface.flx")
endmatch;
debugln$ "Flx interface filename is " + control*.FLX_INTERFACE_FILENAME;
control.CXX_INTERFACE_FILENAME <-
match control*.BUNDLE_DIR with
| Some dir => Filename::join(dir,Filename::basename filebase+".hpp")
| #None => cache_join (config.FLX_OUTPUT_DIR,filebase+".hpp")
endmatch;
debugln$ "C++ interface filename is " + control*.FLX_INTERFACE_FILENAME;
if control*.LINKER_OUTPUT_FILENAME == "" do
if control*.LINKIT == 1 or control*.RUNONLY == 1 do
if control*.STATICLIB == 1 do
var f = filebase+EXT_LIB;
elif control*.STATIC == 0 do // dynamic
if control*.LINKEXE == 1 do
f = filebase+EXT_LIB;
else // DLL
f = filebase+EXT_SHLIB;
done
else
f = filebase+EXT_EXE;
done
else // No link, name specifies object file only.
if control*.STATIC == 1 do
f = filebase+EXT_STATIC_OBJ;
else
f = filebase+EXT_SHARED_OBJ;
done
done
control.LINKER_OUTPUT_FILENAME <- cache_join (config.FLX_CACHE_DIR,f);
debugln$ "Felx writing output binary to " + control*.LINKER_OUTPUT_FILENAME;
elif control*.OUTPUT_FILENAME_WITHOUT_EXTENSION_SPECIFIED == 1 do
if control*.LINKIT == 1 or control*.RUNONLY == 1 do
if control*.STATICLIB == 1 do
control.LINKER_OUTPUT_FILENAME `(+=) EXT_LIB;
elif control*.STATIC == 0 do // dynamic
if control*.LINKEXE == 1 do
control.LINKER_OUTPUT_FILENAME `(+=) EXT_EXE;
else
control.LINKER_OUTPUT_FILENAME `(+=) EXT_SHLIB;
done
else
control.LINKER_OUTPUT_FILENAME `(+=) EXT_EXE;
done
else // No link, name specifies object file only.
if control*.STATIC == 1 do
control.LINKER_OUTPUT_FILENAME `(+=) EXT_STATIC_OBJ;
else
control.LINKER_OUTPUT_FILENAME `(+=) EXT_SHARED_OBJ;
done
done
elif control*.OUTPUT_DIRECTORY_SPECIFIED == 1 do
var basename = Filename::basename (Filename::strip_extension filebase);
if control*.LINKIT == 1 or control*.RUNONLY == 1 do
if control*.STATICLIB == 1 do
control.LINKER_OUTPUT_FILENAME <- control*.LINKER_OUTPUT_FILENAME / basename + EXT_LIB;
elif control*.STATIC == 0 do // dynamic
if control*.LINKEXE == 1 do
control.LINKER_OUTPUT_FILENAME <- control*.LINKER_OUTPUT_FILENAME / basename + EXT_EXE;
else
control.LINKER_OUTPUT_FILENAME <- control*.LINKER_OUTPUT_FILENAME / basename + EXT_SHLIB;
done
else
control.LINKER_OUTPUT_FILENAME <- control*.LINKER_OUTPUT_FILENAME / basename + EXT_EXE;
done
else // No link, name specifies object file only.
if control*.STATIC == 1 do
control.LINKER_OUTPUT_FILENAME <- control*.LINKER_OUTPUT_FILENAME / basename + EXT_STATIC_OBJ;
else
control.LINKER_OUTPUT_FILENAME <- control*.LINKER_OUTPUT_FILENAME / basename + EXT_SHARED_OBJ;
done
done
done
control.LINKER_OUTPUT_FILENAME <- Directory::mk_absolute_filename control*.LINKER_OUTPUT_FILENAME;
control.LINKER_OUTPUT_FILENAME <-
match control*.BUNDLE_DIR with
| Some dir => Filename::join(dir,Filename::basename control*.LINKER_OUTPUT_FILENAME)
| #None => control*.LINKER_OUTPUT_FILENAME
endmatch;
debugln$ "Linker output filename " + control*.LINKER_OUTPUT_FILENAME;
val args = control*.USER_ARGS;
debugln$ "Target program args = "+args.str;
if control*.NOSTDLIB == 1 do
var FLX_STD_LIBS=Empty[string];
else
match control*.FLX_STD_LIBS with
| #Empty => FLX_STD_LIBS = list[string] ("std");
| x => FLX_STD_LIBS = x;
endmatch;
done
debugln$ "Felix standard (cached) libraries: " + str FLX_STD_LIBS;
var STDGRAMMAR = Directory::mk_absolute_filename control*.STDGRAMMAR;
var GRAMMAR_DIR = Directory::mk_absolute_filename control*.GRAMMAR_DIR;
var AUTOMATON = Directory::mk_absolute_filename control*.AUTOMATON;
var DEBUGSWITCH=Empty[string];
if control*.DEBUG == 1 do DEBUGSWITCH=list[string]$ "--debug"; done
var STATIC_ENV=Empty[string];
if control*.DEBUG == 1 do STATIC_ENV=list[string] ("env","FLX_DEBUG=1"); done
debugln$ "RECOMPILE="+str control*.RECOMPILE;
debugln$ "RUNIT="+str control*.RUNIT;
var VERBOSE = Empty[string];
if control*.DEBUG_COMPILER == 1 do
VERBOSE=list[string] "-v";
debugln "Compiler debugging on";
else
VERBOSE=list[string]$ "-q";
debugln "Compiler debugging off";
done
if control*.DEBUG==1 do
control.CCFLAGS <- control*.CCFLAGS+DEBUG_FLAGS;
done
return struct {
var filebase=filebase;
var cpp_filebase=cpp_filebase;
var args = args;
var use_ext = use_ext;
var FLX_STD_LIBS=FLX_STD_LIBS;
var AUTOMATON=AUTOMATON;
var GRAMMAR_DIR=GRAMMAR_DIR;
var STDGRAMMAR=STDGRAMMAR;
var DEBUGSWITCH=DEBUGSWITCH;
var STATIC_ENV=STATIC_ENV;
var VERBOSE = VERBOSE;
};
} // fun cal_depvars
} // class FlxDepvars
The execution manager.¶
This part of the flx tool is responsible for calculating dependencies and actually running the external compilers.
//[flx_run.flx]
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)
{
var cc,ct = 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
);
control.CLEAR_CACHE <- cc;
control.cache_time <- ct;
}
object processing_env(
toolchain_maker: toolchain_config_t -> toolchain_t,
c_compiler_executable: string,
cxx_compiler_executable: string,
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);
}
// case 2 of dflt
var dflt_toolchain_config = (
c_compiler_executable = c_compiler_executable,
cxx_compiler_executable = cxx_compiler_executable,
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 ehandler () {
eprintln$ "Flx: calpackages : failed, temporary ehandler invoked";
System::exit 1;
}
*/
proc calpackages (ehandler:1->0)
{
debugln$ "[flx:calpackages] Calculating package requirements (calpackages_run="+str calpackages_run +")";
if not calpackages_run do
var tc = toolchain_maker dflt_toolchain_config;
var x = FlxPkg::map_package_requirements ehandler
(
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 (ehandler:1->0) : 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.STDIMPORTS + 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 ehandler;
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 (ehandler:1->0) (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;
// FIXME
//System::exit (1);
throw_continuation ehandler;
done
pkg_cflags = mycflags;
done
var tc = toolchain_maker
extend dflt_toolchain_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 (ehandler:1->0) : int =
{
var EXT_SHARED_OBJ = #((toolchain_maker dflt_toolchain_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 ehandler
(
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 ehandler (dvars.cpp_filebase+".cpp", control.LINKER_OUTPUT_FILENAME);
if result != 0 return result;
else
result = cxx_compile_dynamic1 ehandler (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 ehandler (src,dst);
if result != 0 return result;
+= (&control.cppos, dst);
done
return 0;
}
// C++ static (one file)
gen cxx_compile_static (ehandler:1->0) : 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_maker dflt_toolchain_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 ehandler
(
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 ehandler (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 ehandler
(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 ehandler
(dvars.cpp_filebase+".cpp",dvars.cpp_filebase+EXT_STATIC_OBJ);
done
else
return 0;
done
}
// C++ static (many files)
gen cxx_compile_static1 (ehandler:1->0) (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;
// FIXME
System::exit (1);
done
pkg_cflags = mycflags;
done
var tc = toolchain_maker
extend dflt_toolchain_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 (ehandler:1->0) : int =
{
var result = 0;
if control.STATIC == 0 do
debugln "Dynamic linkage";
result = cxx_compile_dynamic ehandler;
else
debugln "Static linkage";
result = cxx_compile_static ehandler;
done
return result;
}
gen ocaml_compile1 (ehandler:1->0) (deps:list[string], s:string) = {
var xqt = dxqt (control.ECHO == 1 or control.DEBUG_FLX);
var result = xqt("ocamlopt.opt -c " + cat " " deps + " "+ s);
C_hack::ignore(result);
return 0;
}
gen ocaml_compile (ehandler:1->0) = {
var deps = Empty[string];
for src in control.ocamls do
if suffix(src,".cmi")
or suffix(src,".cmx")
do
deps+=src;
else
var result = ocaml_compile1 ehandler (deps,src);
if result != 0 return result;
if suffix(src,".mli") do
deps+= src.[..-5]+".cmi";
elif suffix(src,".ml") do
deps+= src.[..-4]+".cmi";
done
done
done
return 0;
}
gen run_ocaml_compiler_if_required (ehandler:1->0) : int =
{
return ocaml_compile ehandler;
}
/*
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 (ehandler:1->0) : 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;
// FIXME
//System::exit (1);
throw_continuation ehandler;
done
pkg_dstrings = FlxPkg::fix2word_flags mydstrings;
done
var tc = toolchain_maker
extend dflt_toolchain_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 (ehandler:1->0) : int =
{
calpackages ehandler;
return cxx_link_shared_library ehandler;
}
// ------------------------------------------------------------------
// Link shared exe
// ------------------------------------------------------------------
gen cxx_link_shared_exe (ehandler:1->0) : 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;
// FIXME
//System::exit (1);
throw_continuation ehandler;
done
pkg_dstrings = FlxPkg::fix2word_flags mydstrings;
done
var tc = toolchain_maker
extend dflt_toolchain_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(ehandler:1->0) : int =
{
calpackages ehandler;
return cxx_link_shared_exe ehandler;
}
// ------------------------------------------------------------------
// Link static exe
// ------------------------------------------------------------------
gen cxx_link_static_exe (ehandler:1->0) : 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;
// FIXME
//System::exit (1);
throw_continuation ehandler;
done
pkg_sstrings = FlxPkg::fix2word_flags mysstrings;
done
var tc = toolchain_maker
extend dflt_toolchain_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(ehandler:1->0) : int =
{
calpackages ehandler;
return cxx_link_static_exe ehandler;
}
// ------------------------------------------------------------------
// Link static (archive) library
// ------------------------------------------------------------------
gen cxx_static_library (ehandler:1->0) : int =
{
var t0 = #Time::time;
var tc = toolchain_maker
extend dflt_toolchain_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$ "Extra ocaml files "+ str control.ocamls;
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(ehandler:1->0) : 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 ehandler;
else
result = cxx_link_shared_library_with_calpackages ehandler;
done
else
debugln "Static linkage";
if control.STATICLIB == 1 do
result = cxx_static_library ehandler;
else
result = cxx_link_static_exe_with_calpackages ehandler;
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 (ehandler:1->0) : 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 (ehandler:1->0) : 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 (ehandler:1->0) : int =
{
calpackages ehandler;
return run_program_dynamic ehandler;
}
gen run_program_if_required (ehandler:1->0) : int =
{
var result = 0;
if control.STATIC == 0 do
debugln$ "Running dynamic program";
result = run_dynamic_with_calpackages ehandler;
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 ehandler;
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(ehandler:1->0) : int = {
var result = 0;
if control.FELIX == 1 do
result = run_felix_compiler_if_required ehandler;
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 ehandler;
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 ehandler;
if result != 0 return result;
else
debugln "C++ compilation (and linking and running) skipped by switch";
done
if control.CCOMPILEIT == 1 do // use this switch for Ocaml compiles too
result = run_ocaml_compiler_if_required ehandler;
else
debugln "Ocaml compilation skipped by switch";
done
if control.LINKIT == 1 do
result = run_linker_if_required ehandler;
if result != 0 return result;
else
debugln "Link step skipped by switch";
done
if control.RUNIT == 1 do
result = run_program_if_required ehandler;
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;
}
}
The {flx} tool.¶
//[flx.flx]
include "std/felix/config";
include "std/felix/flx_cache";
include "std/felix/flx_pkg";
include "std/felix/flx_flxg";
include "std/felix/flx_cxx";
include "std/felix/flx/flx_control";
include "std/felix/flx/flx_cmdopt";
include "std/felix/flx/flx_depvars";
include "std/felix/flx/flx_run";
include "std/felix/toolchain_config";
include "std/felix/toolchain_interface";
open FlxCache;
fun startlib (x:string) =
{
return x in RE2(" *(fun|proc|var|val|gen|union|struct|typedef).*\n");
}
// MOVE LATER!
proc repl()
{
nextline:>
print "> "; fflush stdout;
var text = readln stdin;
if feof(stdin) return;
if startlib(text) goto morelibrary;
goto executable;
morelibrary:>
print ".. "; fflush stdout;
var more = readln stdin;
if feof(stdin) return;
if more == "\n" goto saveit;
text += more;
goto morelibrary;
saveit:>
var dlibrary = load("library.flx");
dlibrary += text;
save("library.flx",dlibrary);
goto nextline;
executable:>
var session = load("session.flx");
session += text;
save ("session.flx", session);
dlibrary = load("library.flx");
var torun = dlibrary + text;
save ("cmd.flx", torun);
}
// Felix version of THIS program (NOT the one being installed
// if you're using flx to install Felix)
class Flx
{
gen flx_processing
(
config:&Config::config_type,
control:&FlxControl::control_type,
loopctl:&FlxControl::loopctl_type,
args:list[string]
) : int =
{
var result = 0;
fun / (a:string, b:string) => Filename::join (a,b);
FlxCmdOpt::processing_stage1 (config,control,loopctl,varray[string] args);
if control*.VALIDATE_CACHE == 1 do
check_cache(config, control);
done
if
loopctl*.base == "" and
control*.INREGEX == ""
and not control*.CMDLINE_INPUT
do
if control*.CLEAR_CACHE != 1 do
println "usage: flx [options] filename";
// TOP LEVEL FLX, OK
System::exit(1);
done
// TOP LEVEL FLX, OK
System::exit(0);
done
var pkgconfig = FlxPkgConfig::FlxPkgConfigQuery$ config*.FLX_CONFIG_DIRS;
proc ehandler () {
eprintln$ "Flx: default ehandler: temporary ehandler invoked";
System::exit 1;
}
var toolchain_name =
// toolchain pkg 1
if control*.FLX_TOOLCHAIN == "" then pkgconfig.getpkgfield1 ehandler ("toolchain", "toolchain")
else control*.FLX_TOOLCHAIN
;
var c_compiler_executable =
pkgconfig.getpkgfielddflt ehandler (toolchain_name+"_c_compiler_executable", "compiler")
;
var cxx_compiler_executable =
pkgconfig.getpkgfielddflt ehandler (toolchain_name+"_cxx_compiler_executable", "compiler")
;
var toolchain_maker =
match toolchain_name with
| x =>
Dynlink::load-plugin-func1 [toolchain_t,toolchain_config_t] ( dll-name=x, setup-str="")
endmatch
;
//println$ "[flx] Toolchain set to " + toolchain_name;
if control*.INREGEX != "" do
begin
//control.USER_ARGS <- Shell::quote_arg(loopctl*.progname) + ' ' + control*.USER_ARGS;
// this is a hack because -- argument translates to empty program name ..
// and also if there is no name in that slot ..
if loopctl*.progname != "" do
control.USER_ARGS <- loopctl*.progname ! control*.USER_ARGS;
done
if control*.INDIR == "" do control.INDIR <- "."; done
var regex = RE2 control*.INREGEX;
if not regex.ok do
eprintln$ "Malformed regex " + control*.INREGEX;
result = 1;
goto endoff;
done
var files = FileSystem::regfilesin (control*.INDIR, regex);
var n = files.len.int;
println$ "Processing " + files.len.str + " files";
var i = 1;
var pass = 0;
var fail = 0;
files = sort files;
for file in files do
var arg = Filename::join (control*.INDIR, file);
var path,ext = Filename::split_extension(arg);
loopctl.path <- path;
loopctl.ext <- ext;
var dir,base = Filename::split1(loopctl*.path);
loopctl.dir <- dir;
loopctl.base <- base;
// temporary hack, to force reset of the linker filename, stdout, and expect
// file names in cal_depvars so they depend on the current file.
control.LINKER_OUTPUT_FILENAME <- "";
control.STDOUT <- "";
control.EXPECT <- "";
control.STDIN <- "";
var dvars = FlxDepvars::cal_depvars(toolchain_maker,c_compiler_executable, cxx_compiler_executable, *config,control,*loopctl);
println$ f"Processing [%02d/%02d]: %S" (i, n, file);
var pe = processing_env(toolchain_maker,c_compiler_executable, cxx_compiler_executable, *config,*control,dvars);
call_with_trap {
proc ehandler() {
eprintln("BATCH MODE ERROR HANDLER");
result = 1;
goto err;
}
result = pe.runit(ehandler);
err:>
};
if result == 0 do ++pass; else ++fail; done
if control*.NONSTOP==0 and result != 0 goto endoff;
++i;
collect();
done
println$ f"Batch result (%02d OK + %02d FAIL)/%2d" (pass, fail,n);
end
elif control*.REPL_MODE do
begin
again:>
repl();
if not feof (stdin) do
var dvars = FlxDepvars::cal_depvars(toolchain_maker,c_compiler_executable, cxx_compiler_executable, *config,control, *loopctl);
var pe = processing_env(toolchain_maker,c_compiler_executable, cxx_compiler_executable, *config,*control,dvars);
result = pe.runit(ehandler);
goto again;
else
println$ "Bye!";
// TOP LEVEL REPL, OK
System::exit 0;
done
end
else
begin
if control*.SHOWCODE == 1 do
var prg =
(if dvars.use_ext == "" then "// No file "+dvars.filebase+".(flx|fdoc) found"
else load(dvars.filebase+"."+dvars.use_ext)
);
print prg;
done
var dvars = FlxDepvars::cal_depvars(toolchain_maker,c_compiler_executable, cxx_compiler_executable, *config,control, *loopctl);
var pe = processing_env(toolchain_maker,c_compiler_executable, cxx_compiler_executable, *config,*control,dvars);
result = pe.runit(ehandler);
end
done
endoff:>
return result;
}
gen runflx(args:list[string]) : int =
{
var config = #Config::std_config;
var control = #FlxControl::dflt_control;
var loopctl = #FlxControl::init_loopctl;
return flx_processing(&config, &control, &loopctl, args);
}
}
Bootflx¶
This is supposed to be the same as the standard flx
tool, except it includes all the required source code
which means it takes a very long time to compile.
//[bootflx.flx]
include "std/felix/config";
include "std/felix/flx_cache";
include "std/felix/flx_pkg";
include "std/felix/flx_flxg";
include "std/felix/flx_cxx";
include "std/felix/flx/flx_control";
include "std/felix/flx/flx_cmdopt";
include "std/felix/flx/flx_depvars";
include "std/felix/flx/flx_run";
include "std/felix/toolchain_config";
include "std/felix/toolchain_interface";
include "std/felix/toolchain/clang_macosx";
include "std/felix/toolchain/clang_iOS_generic";
include "std/felix/toolchain/clang_linux";
include "std/felix/toolchain/gcc_macosx";
include "std/felix/toolchain/gcc_linux";
include "std/felix/toolchain/msvc_win";
open FlxCache;
// Felix version of THIS program (NOT the one being installed
// if you're using flx to install Felix)
class BootFlx
{
gen flx_processing
(
config:&Config::config_type,
control:&FlxControl::control_type,
loopctl:&FlxControl::loopctl_type,
args:list[string]
) : int =
{
var result = 0;
fun / (a:string, b:string) => Filename::join (a,b);
FlxCmdOpt::processing_stage1 (config,control,loopctl,varray[string] args);
if control*.VALIDATE_CACHE == 1 do
check_cache(config, control);
done
if loopctl*.base == "" and control*.INREGEX == "" do
if control*.CLEAR_CACHE != 1 do
println "usage: flx [options] filename";
// TOP LEVEL FLX, OK
System::exit(1);
done
// TOP LEVEL FLX, OK
System::exit(0);
done
proc ehandler () {
eprintln$ "BOOTFLX: Flx_pkgconfig getpkgfiled1 failed, temporary ehandler invoked";
System::exit 1;
}
var dbdir = config*.FLX_TARGET_DIR / "config";
var pkgconfig = FlxPkgConfig::FlxPkgConfigQuery$ list[string] dbdir;
var toolchain_name =
// toolchain pkg 2
if control*.FLX_TOOLCHAIN == "" then pkgconfig.getpkgfield1 ehandler ("toolchain", "toolchain")
else control*.FLX_TOOLCHAIN
;
var c_compiler_executable = "";
c_compiler_executable =
pkgconfig.getpkgfielddflt ehandler (toolchain_name+"_c_compiler_executable", "compiler")
;
var cxx_compiler_executable = "";
cxx_compiler_executable =
pkgconfig.getpkgfielddflt ehandler (toolchain_name+"_cxx_compiler_executable", "compiler")
;
var toolchain_maker =
match toolchain_name with
| "toolchain_clang_macosx" => toolchain_clang_macosx
// not required in bootstrap, but the ONLY way to check for type errors ..
| "toolchain_iphoneos" => toolchain_clang_apple_iPhoneOS_armv7_arm64
| "toolchain_iphonesimulator" => toolchain_clang_apple_iPhoneSimulator
| "toolchain_clang_linux" => toolchain_clang_linux
| "toolchain_gcc_macosx" => toolchain_gcc_macosx
| "toolchain_gcc_linux" => toolchain_gcc_linux
| "toolchain_msvc_win" => toolchain_msvc_win
| x =>
Dynlink::load-plugin-func1 [toolchain_t,toolchain_config_t] ( dll-name=x, setup-str="")
endmatch
;
if control*.INREGEX != "" do
begin
control.USER_ARGS <- Shell::quote_arg(loopctl*.progname) + ' ' + control*.USER_ARGS;
if control*.INDIR == "" do control.INDIR <- "."; done
var regex = RE2 control*.INREGEX;
if not regex.ok do
eprintln$ "Malformed regex " + control*.INREGEX;
result = 1;
goto endoff;
done
var files = FileSystem::regfilesin (control*.INDIR, regex);
var n = files.len.int;
println$ "Processing " + files.len.str + " files";
var i = 1;
for file in files do
var arg = Filename::join (control*.INDIR, file);
var path,ext = Filename::split_extension(arg);
loopctl.path <- path;
loopctl.ext <- ext;
var dir,base = Filename::split1(loopctl*.path);
loopctl.dir <- dir;
loopctl.base <- base;
// temporary hack, to force reset of the linker filename, stdout, and expect
// file names in cal_depvars so they depend on the current file.
control.LINKER_OUTPUT_FILENAME <- "";
control.STDOUT <- "";
control.EXPECT <- "";
var dvars = FlxDepvars::cal_depvars(toolchain_maker,c_compiler_executable, cxx_compiler_executable, *config,control,*loopctl);
println$ f"Processing [%02d/%02d]: %S" (i, n, file);
var pe = processing_env(toolchain_maker,c_compiler_executable, cxx_compiler_executable, *config,*control,dvars);
result = pe.runit(ehandler);
if result != 0 goto endoff;
++i;
done
end
else
begin
if control*.SHOWCODE == 1 do
var prg =
(if dvars.use_ext == "" then "// No file "+dvars.filebase+".(flx|fdoc) found"
else load(dvars.filebase+"."+dvars.use_ext)
);
print prg;
done
var dvars = FlxDepvars::cal_depvars(toolchain_maker,c_compiler_executable, cxx_compiler_executable, *config,control, *loopctl);
var pe = processing_env(toolchain_maker,c_compiler_executable, cxx_compiler_executable, *config,*control,dvars);
result = pe.runit(ehandler);
end
done
endoff:>
return result;
}
gen runflx(args:list[string]) : int =
{
println$ "[bootflx] " + strcat " " args;
var config = #Config::std_config;
var control = #FlxControl::dflt_control;
var loopctl = #FlxControl::init_loopctl;
return flx_processing(&config, &control, &loopctl, args);
}
}
Plugin Client.¶
Flx is also available as a plugin. This wrapper loads it on demand so it is easy to call flx from any Felix program.
//[flx_plugin_client.flx]
class Flx_client {
var runflx : list[string] -> int;
proc setup ()
{
runflx = Dynlink::load-plugin-func1 [int,list[string]] ( dll-name="flx_plugin", setup-str="");
}
}
Bootstrap Felix.¶
The same as the ordinary flx
command, except the standard
toolchains are compiled in directly.
//[bootflx_tool.flx]
include "std/felix/flx/bootflx";
println$ "BOOTFLX";
System::pexit$ BootFlx::runflx #System::args;