/********************************** COOK EXECUTION FLAGS **********************************/ set mkdir; /* make required directories automatically */ set tell-position; /* be verbose about where the call is coming from */ setenv SHELL = /bin/sh; /* explicit shell */ true = "1"; /* give credence to truth */ false = "0"; /* give credence to lies */ /********************************** CLOBBER & CLEAN UP **********************************/ build.root = build; /* root directory for build artifacts */ dist.root = dist; /* root directory for programs and libraries */ /* Clean up the build space. Easy when all build artifacts are contained in build. */ clean: { function print "Removing build artifacts:"; rm -rfv [build.root]; } /* Clean and then clobber the build artifacts in dist */ clobber:clean { function print "Removing distribution artifacts:"; rm -rfv [dist.root]; } /********************************** COOKBOOK INCLUDE LOGIC **********************************/ cookbook = [dirname [__FILE__]]; /* directory of cook related files */ config = config.cook; /*config for the cook script.*/ /* if a config file exists, include it */ #if [exists [config]] #include [config] #endif /*TODO: Try and figure out the compiler from an environment variable or testing for compilers?*/ /*The includes must occur during the preprocessing stage due to #include limitations. *unhappy programmer*/ #if [defined fc] #if [exists [cookbook]/compiler/[fc].cook] #include [cookbook]/compiler/[fc].cook #else fail "Fortran compiler not supported:" [fc]; #endif #endif /* read the defaults after the real compiler config, defaults only set if not otherwise */ #include [cookbook]/compiler/defaults.cook /********************************** INITIALIZE VARIABLES **********************************/ if [not [defined programs]] then{ programs =; /* empty list of programs */ } src.dir = src; /* source files */ inc.dir = [src.dir]/inc; /* include files */ test.dir = tests; /* test scripts & source files */ build.dir = [build.root]/[fc]; /* build artifacts */ dep.dir = [build.dir]/dep; /* dependency build artifacts */ obj.dir = [build.dir]/obj; /* object build artifacts */ mod.dir = [build.dir]/mod; /* module build artifacts */ dist.dir = [dist.root]/[fc]; /* target executables */ /********************************** FILE EXTENSIONS **********************************/ f = .f; /* fortran fixed form */ f90 = .f90; /* fortran free form */ dep = .dep; /* dependency */ mod = .mod; /* module */ lib = .a; /* library */ obj = .o; /* object */ exe = ; /* executable */ tmp = .tmp; /* temp file */ inc = .inc; /* include file */ prog = .program; /* dummy program */ /* tools TODO: add if not defined statements before setting the defaults */ fc_flags = ; /* fortran compiler flags */ fc_oflags = ; /* fortran optimization compiler flags */ fc_dflags = ; /* fortran debug compiler flags */ ld_flags = ; /* fortran linker flags */ c_incl = [find_command c_incl]; /* dependency finder */ /* find all the source files */ manifest = [stripdot [collect find [src.dir] -print]]; /* Every file*/ f_src = [filter %0%[f] [manifest]]; /* Every fixed form fortran file */ f90_src = [filter %0%[f90] [manifest]]; /* Every free form fortran file */ inc_src = [filter %0%[inc] [manifest]]; /* Every include file */ all_src = [f_src] [f90_src] [inc_src]; /* combine explicit source files */ all_dep = [addsuffix [dep] [fromto [src.dir]%0% [dep.dir]%0% [all_src]]]; /* mapped to dep.dir and dep extension added */ /* * Include all the dependency files. If a source file is newer than its * dependency file then the dependencies are updated. Then all the * dependency files are included into the cook. Statements in the dep files * are executed when included. */ #include-cooked [all_dep] /********************************** PRINT LOG HEADERS **********************************/ function print "Datetime:" [collect date]; function print "Computer:" [os node]; function print "User:" [collect whoami]; function print "System:" [os system]; function print "Arch:" [os machine]; function print "Compiler:" [fc]; function print [fc_version]; function print "Linker:" [ld]; function print [ld_version]; function print "Archiver:" [ar]; function print [ar_version]; /********************************** CORE RECIPIES **********************************/ /* default target, cooks all defined programs */ all:[strip [programs]] set default {} /* stub for the test target */ test:{ fail "There are no test scripts defined."; } /* stub for the documentation target */ doc:{ fail "Unable to generate documentation."; } /* validate configuration */ validate:{ if [not [defined fc]] then{ fail "Fortran compiler must be explicity set:" [self] "configure fc=compiler"; } } /* write out the config */ configure:{ local result =; result +="if [not [defined fc]] then{"; result += "fc="[fc]";"; result += "}"; function write [config] [result]; cook validate; } unconfigure:{ rm [config]; } /* inc dependency */ [dep.dir]%0%[inc][dep] : [src.dir]%0%[inc]{ /* incl use mod prog */ function update-dep [need] [target] [true] [true] [false] [false]; } /* fixed form dependency */ [dep.dir]%0%[f][dep] : [src.dir]%0%[f]{ /* incl use mod prog */ function update-dep [need] [target] [true] [true] [true] [true]; } /* free form dependency */ [dep.dir]%0%[f90][dep] : [src.dir]%0%[f90]{ /* incl use mod prog */ function update-dep [need] [target] [true] [true] [true] [true]; } /* fixed form compile */ [obj.dir]%0%[obj]: [src.dir]%0%[f] { /* prepare a local list of includes */ local incs = [stringset [src.dir]%0 [inc.dir]]; if [fc_inc_join] then { /* combine the incs into a single list with the separator */ incs = [unsplit [fc_inc_sep] [incs]]; } [fc] [fc_flags] [addprefix [fc_inc_flag] [incs]] [addprefix [fc_mod_flag] [mod.dir]] [fc_compile_flag] [head [need]] [fc_out_flag] [target]; } /* free form compile */ [obj.dir]%0%[obj]: [src.dir]%0%[f90] { /* prepare a local list of includes */ local incs = [stringset [src.dir]%0 [inc.dir]]; if [fc_inc_join] then { /* combine the incs into a single list with the separator */ incs = [unsplit [fc_inc_sep] [incs]]; } [fc] [fc_flags] [addprefix [fc_inc_flag] [incs]] [addprefix [fc_mod_flag] [mod.dir]] [fc_compile_flag] [head [need]] [fc_out_flag] [target]; } /* modules The dep cascades will have already caused the mod to be created when the obj was compiled. :) Magic! */ [mod.dir]%0%[mod]:{} /* link a fortran object file into an executable */ [build.dir]%0%[exe]: [obj.dir]%0%[obj] { /*filter the ingredients to only object files*/ set match-mode-regex; local need2 = [filter .*\.[obj] [need]]; set match-mode-cook; /*TODO: if the need2 is greater than a certain count the use a temp file */ /* prepare a local list of includes */ local incs = [stringset [src.dir]%0 [inc.dir]]; if [ld_inc_join] then { /* combine the incs into a single list with the separator */ incs = [unsplit [ld_inc_sep] [incs]]; } [ld] [ld_flags] [addprefix [ld_inc_flag] [incs]] [addprefix [ld_mod_flag] [mod.dir]] [need2] [ld_out_flag] [target]; } /* Copies the cascaded program into [dist.dir]*/ %:%[prog] if [in [target] [programs]] { if [not [exists [dist.dir]]] then{ mkdir -p [dist.dir]; } /* The first ingredient is a dummy placeholder */ cp [word 2 [need]] [dist.dir]/[target][exe]; } /*Dummy recipie allows for cascading programs*/ %[prog]:{} /********************************** DEPENDENCY FUNCTIONS **********************************/ /* standard way to check dependency graph for different types of fortran or include files */ function update-dep = { local dep_src = [@1]; local dep_target = [@2]; local scan-incl = [@3]; local scan-used = [@4]; local scan-provided = [@5]; local scan-program = [@6]; local result =; /* result to be written to the dep file */ dep_obj = [toobj [dep_src]]; /* scan for includes */ if [scan-incl] then { function print "Scanning" [need] "for included files."; result += [collect [c_incl] -nc -ns -nrec --language\=optimistic /*[cc_include_flags]*/ [head [need]] [addprefix "-I" [src.dir]%0] [addprefix "-I" [inc.dir]] -prefix "'cascade "[dep_obj]" ='" -suffix "';'"]; } /* scan for modules used by this file */ if [scan-used] then { local mods_used = [modules-used [dep_src]]; if [mods_used] then{ result += "cascade" [dep_obj] "=" [mods_used] ";"; } } /* scan for modules provided by this file */ if [scan-provided] then { local mods_provided = [prepost [mod.dir]"/" [mod] [modules-provided [dep_src]]]; if [mods_provided] then{ local provided =; loop provided = [mods_provided] { result += "cascade" [provided] "=" [dep_obj] ";"; } } } /* scan for program provided by this file */ if [scan-program] then { local prog_provided = [program-provided [dep_src]]; if [prog_provided] then{ result += "cascade" [addsuffix [prog] [prog_provided]] "=" [fromto [obj.dir]%0%[obj] [build.dir]%0%[exe] [dep_obj]] ";"; result += "programs+="[prog_provided]";"; } } /* Always fresh */ if [exists [dep_target]] then{ rm -f [dep_target]; } /* output */ function write [dep_target] [result]; } /* function to collect modules used. */ function modules-used = { set errok; /*no error required because grep returns 1 for no match which is okay sometimes*/ local need = [@1]; function print "Scanning" [need] "for used modules."; /* Finds all uncommented use statements and cascades an include for the module*/ local used = [collect grep -RPioh "'^\\s*use\\s+\\w+'" [need] | sed "'s/^\\s*use\\s*//i'" | awk "'{print \""[mod.dir]"/\" tolower($0)\""[mod]"\"}'"]; return [used]; } /* function to collect modules provided. */ function modules-provided = { set errok; /*no error required because grep returns 1 for no match which is okay sometimes*/ local need = [@1]; function print "Scanning" [need] "for provided modules."; local provided = [collect grep -RPio "'^\\s*module\\s+\\w+'" [need] | sed "'s/\\s*module\\s*//i'"]; return [downcase [provided]]; } /* function to collect program provided. */ function program-provided = { set errok; /*no error required because grep returns 1 for no match which is okay sometimes*/ local need = [@1]; function print "Scanning" [need] "for program."; local provided = [collect grep -RPio "'^\\s*program\\s+\\w+'" [need] | sed "'s/\\s*program\\s*//i'"]; return [downcase [provided]]; } /*function to change extensions to [obj]*/ function toobj = { local src = [@1]; /* change any extension to obj */ set match-mode-regex; local result = [fromto \\(.*\\)\\.f.* \\1[obj] [src]]; /* change src.dir to obj.dir */ set match-mode-cook; result = [fromto [src.dir]%0% [obj.dir]%0% [result]]; return [result]; }