sheepy

build system (sheepy) and package manager (spm) for C
git clone https://spartatek.se/git/sheepy.git
Log | Files | Refs | README | LICENSE

sheepy.c (80668B)


      1 // MIT License
      2 //
      3 // Copyright (c) 2026 Remy Noulin
      4 //
      5 // Permission is hereby granted, free of charge, to any person obtaining a copy
      6 // of this software and associated documentation files (the "Software"), to deal
      7 // in the Software without restriction, including without limitation the rights
      8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      9 // copies of the Software, and to permit persons to whom the Software is
     10 // furnished to do so, subject to the following conditions:
     11 //
     12 // The above copyright notice and this permission notice shall be included in all
     13 // copies or substantial portions of the Software.
     14 //
     15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     21 // SOFTWARE.
     22 //
     23 //
     24 // sheepy compilation system
     25 
     26 #include "libsheepyObject.h"
     27 #undef inc
     28 #include "sheepy.h"
     29 #include "common.h"
     30 
     31 #define internal static
     32 
     33 #include <sys/stat.h>
     34 #include <ctype.h>
     35 #include <stdio.h>
     36 #include <dirent.h>
     37 #include <libgen.h>
     38 #include <stdbool.h>
     39 #include <string.h>
     40 #include <stdlib.h>
     41 #include <unistd.h>
     42 
     43 #undef inc
     44 #if (__TERMUX__)
     45 #elif (__sun__)
     46 #else
     47 #endif
     48 #if (__HAIKU__)
     49 #else
     50 #endif
     51 #ifndef unitTest
     52 #endif
     53 int MAIN(int ARGC, char** ARGV);
     54 char *getInclude(smallStringt *L);
     55 void addFileToCompile(char *actualDir, const char *cFileName, char *oPath, smallDictt *toCompile);
     56 void storeHeaderDep(smallArrayt *copyHeaderDepToBuildPath, char *bPath, struct stat hst);
     57 void copyHeaderToBuildPath(char *bPath, struct stat hst);
     58 void copyHeaderDates(char *actualDir, smallArrayt *hDeps, char *buildPath);
     59 bool symlinkFile(char *path, char *buildPath);
     60 bool isC(const char *path);
     61 bool isH(const char *path);
     62 bool isCP(const char *path);
     63 bool isCH(const char *path);
     64 void generateC(char *src, char *bpath);
     65 void generateCBuffers(smallArrayt *sheepySrc, smallArrayt *cCode);
     66 void splash(void);
     67 
     68 int argc; char **argv;
     69 
     70 #include "parser/EasyLexer.h"
     71 #include "parser/parser.h"
     72 
     73 // when the LIBSHEEPY environment variable exists, it replaces the default libsheepy location
     74 #if (__TERMUX__)
     75 #define LIBSHEEPY_DIR            "/data/data/com.termux/files/usr/lib"
     76 #define LIBSHEEPY_INC_DIR        "/data/data/com.termux/files/usr/include"
     77 #elif (__sun__)
     78 #define LIBSHEEPY_DIR            "/usr/lib"
     79 #define LIBSHEEPY_INC_DIR        "/usr/include"
     80 #else
     81 #define LIBSHEEPY_DIR            "/usr/local/lib"
     82 #define LIBSHEEPY_INC_DIR        "/usr/local/include"
     83 #endif
     84 
     85 #define GCC   "gcc"
     86 
     87 #if (__HAIKU__)
     88 #define PTHREAD   ""
     89 #else
     90 #define PTHREAD   "-pthread"
     91 #endif
     92 
     93 int programResult;
     94 
     95 /**
     96  * build and execute program
     97  * sheepy help lists the options
     98  *
     99  * The program is built in ~/.sheepy/build/realpathToMain
    100  */
    101 #ifndef unitTest
    102 // Remove main when running the unit tests
    103 #define MAIN   main
    104 #endif
    105 int MAIN(int ARGC, char** ARGV) {
    106   char *dum = NULL;
    107   char *mainFilename = NULL;
    108   char *homedir = NULL;
    109   char *libsheepyDir = NULL;
    110   char *libsheepyIncDir = NULL;
    111   smallStringt *bin = NULL;
    112 
    113   argc = ARGC; argv = ARGV;
    114   char *libsheepy = "libsheepy.a";
    115 
    116   // variables:
    117   // dum:           temporary string for selecting type in generics
    118   // mainFilename:  main filename as given on the command line
    119   // homedir:       real sheepy homedir path
    120   // libsheepyDir:  runtime libsheepy location. Default LIBSHEEPY_DIR or env("LIBSHEEPY")
    121   // programResult: result from compilation, linking, program execution, 0 is success, not 0 is failure
    122 
    123   // TODO
    124   // split main into smaller functions
    125   //
    126   // Steps
    127   // load sheepy configuration
    128   // set libsheepy location
    129   // process parameters for sheepy
    130   // get basename (rootMain)
    131   // main source folder (dir)
    132   // load main source (mainCode)
    133   // detect execute bit
    134   // if yes then run (run)
    135   // compile
    136   // link and run
    137   // clean executable
    138 
    139   // load sheepy configuration - to create config.yml even when there is no command
    140   smallJsont *cfgJ = loadSheepyConfig(&homedir);
    141   smallDictt *cfgD = getTopG(cfgJ, rtSmallDictt);
    142   //putsG(cfgJ);
    143 
    144   // set libsheepy location
    145   libsheepyDir = getenv("LIBSHEEPY");
    146 
    147   if (!libsheepyDir) {
    148     // set default libsheepy location
    149     libsheepyDir    = LIBSHEEPY_DIR;
    150     libsheepyIncDir = LIBSHEEPY_INC_DIR;
    151   }
    152   else {
    153     libsheepyIncDir = libsheepyDir;
    154   }
    155 
    156   dum = catS(libsheepyDir, "/libsheepy.a");
    157   if (!fileExists(dum)) {
    158     printf("\n" BLD RED "Libsheepy not found: %s not accessible" RST "\nIf libsheepy in another directory, set LIBSHEEPY to libsheepy path, for example: export LIBSHEEPY=/home/$USER/libsheepy/release/\n", dum); {
    159     free(dum);
    160     XFAILURE
    161   }
    162     }
    163   freen(dum);
    164 
    165   // success result by default
    166   programResult                  = 0;
    167 
    168   // process parameters for sheepy
    169   // when any compile parameter is given, dont execute the program
    170   // -l compileLib=1 build the dynamic and static libraries
    171   sheepyParams.compileLib        = 0;
    172   // -c compileOnly=1 build and dont execute the program
    173   sheepyParams.compileOnly       = 0;
    174   // set to 0 when main is detected in main source file
    175   // 1 - only objects are compiled, linking is not done
    176   // no effect when compileLib=0
    177   sheepyParams.dontCompileExe    = 1;
    178   sheepyParams.clean             = 0;
    179   sheepyParams.test              = 0;
    180   sheepyParams.memcheck          = 0;
    181   sheepyParams.libsheepyMemcheck = 0;
    182   sheepyParams.asan              = 0;
    183   sheepyParams.libsheepyAsan     = 0;
    184   sheepyParams.new               = 0;
    185   sheepyParams.showExe           = 0;
    186   sheepyParams.removeBuild       = 0;
    187   sheepyParams.parallel          = 1;
    188   sheepyParams.genTest           = 0;
    189   sheepyParams.debugExe          = 0;
    190 
    191   // parallel has to be defined and 0 to disable parallel compilation
    192   if (hasG(cfgD, "parallelSheepy") && !getG(cfgD, rtI32, "parallelSheepy")) {
    193     sheepyParams.parallel = 0;
    194   }
    195 
    196   // enable crash debug
    197   initLibsheepy(argv[0]);
    198   setLogMode(LOG_PROGNDATE);
    199 
    200   // initialize child process list
    201   staticArrayInit(jobs);
    202 
    203   if (argc < 2 || (argv[1][0] == '-' && argc == 2)) {
    204     // no argument or one option argument
    205     // read package.yml
    206     if (fileExists(PACKAGE)) {
    207       createSmallJson(packageyml);
    208       readFileG(&packageyml, PACKAGE);
    209       bin = getNDupG(&packageyml, rtSmallStringt, "bin");
    210       freeG(&packageyml);
    211   }
    212     }
    213 
    214   if (argc < 2) {
    215     // default run program or compile the library in package.yml
    216     if (bin) {
    217       var libHeader = dupG(bin);
    218       if (fileExistsG(setG(libHeader, -1, 'h'))) {
    219         // compile lib
    220         sheepyParams.compileLib  = 1;
    221       }
    222       else {
    223         // compile program
    224         sheepyParams.compileOnly = 1;
    225       }
    226       terminateG(libHeader);
    227       mainFilename = normalizePath(ssGet(bin));
    228       terminateG(bin);
    229       goto sheepyRun;
    230     }
    231     splash();
    232     printf("Nothing to do. sheepy -h for help\n"); {
    233     printf("\n");
    234     goto help;
    235   }
    236     }
    237 
    238   // after this line, argc > 1, there is at least one argument
    239 
    240   if (eqS(argv[1], "-h") || eqS(argv[1], "--help") || eqS(argv[1], "help")) {
    241     terminateG(bin);
    242     splash();
    243     help:
    244     putsG(BLD GRN "Sheepy Help (build system)" RST);
    245     putsG("  " UDL YLW "Environment" RST);
    246     if (!startsWithG(argv[0], "/usr/local/bin/")) {
    247       putsG("  sheepy is not running from /usr/local/bin/. To run this sheepy, the path to the sheepy executable needs to be in $PATH:");
    248       char *dir = shDirname(getRealProgPath());
    249       printf(YLW"  export PATH=%s:$PATH or setenv PATH %s:$PATH\n"RST, dir, dir);
    250       free(dir);
    251     }
    252 
    253     putsG("  when the LIBSHEEPY environment variable exists, it replaces the default libsheepy location");
    254     if (libsheepyIncDir != libsheepyDir) {
    255       // give an example setup
    256       putsG(YLW "  export LIBSHEEPY=/home/user/git/tmp/libsheepy/release or setenv LIBSHEEPY /usr/home/user/git/tmp/libsheepy/release" RST);
    257       printf("  Current libsheepy location is " UDL "%s" RST " and " UDL "%s" RST "\n", libsheepyDir, libsheepyIncDir);
    258     }
    259     else {
    260       // give current setup
    261       printf(YLW "  export LIBSHEEPY=%s or setenv LIBSHEEPY %s\n" RST, libsheepyDir, libsheepyDir);
    262       printf("  Current libsheepy location is " UDL "%s" RST "\n", libsheepyDir);
    263     }
    264     putsG("  The configuration is located in " UDL HOME_DIR CONFIG_NAME RST);
    265     char *dum = expandHome(HOME_DIR CONFIG_NAME);
    266     if (!fileExists(dum)) {
    267       printf("  %s missing.", dum);
    268     }
    269     printf("  The build directory is located in " UDL "%sbuild/" RST "\n", HOME_DIR);
    270     printf("  The local package repository is located in " UDL "%s" RST "\n", findLocalPackageDir(cfgD));
    271     if (dum) {
    272       char *p = formatS("%s/%s", getG(cfgD, rtChar, "system"), PACKAGE_DIR);
    273       pErrorNULL(iNormalizePath(&p));
    274       pErrorNULL(iExpandHome(&p));
    275       printf("  The global package repository is located in " UDL "%s" RST "\n", p);
    276       free(p);
    277       p = formatS("%s/%s", getG(cfgD, rtChar, "system"), "bin");
    278       pErrorNULL(iNormalizePath(&p));
    279       pErrorNULL(iExpandHome(&p));
    280       printf("  Commands from packages are added in " UDL "%s" RST "\n", p);
    281       free(p);
    282     }
    283     free(dum);
    284     if (hasG(cfgD, "parallelSpm") && !getG(cfgD, rtI32, "parallelSpm")) {
    285       puts(BLD RED "  Parallel package processing is disabled." RST);
    286     }
    287     if (hasG(cfgD, "parallelSheepy") && !getG(cfgD, rtI32, "parallelSheepy")) {
    288       puts(BLD RED "  Parallel compilation is disabled." RST);
    289     }
    290     put
    291     putsG("For spm package manager help, run: spm help");
    292     put
    293     putsG(BLD CYN"  NO ARGUMENT"RST"     Compile bin executable or compile library from package.yml");
    294     putsG(BLD CYN"  PROGNAME.c"RST"      No option: compile and run executable");
    295     put
    296     putsG(BLD CYN"  -c (PROGNAME.c)"RST"  Compile executable in argument or bin from package");
    297     putsG(BLD CYN"  -e (PROGNAME.c)"RST"  Show executable path for argument or bin from package"); {
    298     putsG(BLD CYN"  -l (PROGNAME.c)"RST"  Compile static and dynamic libraries for argument or bin from package"); {
    299     putsG(BLD CYN"  -d (PROGNAME.c)"RST"  Delete intermediary files for argument or bin from package"); {
    300     putsG(BLD CYN"  -t (PROGNAME.c)"RST"  Compile executable in argument or testBin from package using the test compiler and test linker flags");
    301     putsG(BLD CYN"  -m (PROGNAME.c)"RST"  Compile executable in argument or memcheckBin from package using the memcheck compiler and linker flags and disabling container recycling in libsheepy");
    302     putsG(BLD CYN"  -a (PROGNAME.c)"RST"  Compile executable in argument or asanBin from package using the asan compiler and linker flags and disabling container recycling in libsheepy");
    303     putsG(BLD CYN"  -n PROGNAME(.c)"RST"  New source program from template");
    304     putsG(BLD CYN"  -r"RST"               Remove all temporary build files in ~/.sheepy/build");
    305     putsG(BLD CYN"  -C CLASSNAME"RST"     Generate C templates for classname (classnames start with lowercase letters, the first char in classname is lowered)"); {
    306     putsG(BLD CYN"  -T"RST"               Generate C unit test templates (using the libcheck test library), the generated filename is set with testBin in "PACKAGE);
    307     putsG(BLD CYN"  -g (PROGNAME.c)"RST"  Debug already compiled executable with debuggerCmd in config.yml");
    308     putsG(BLD CYN"  -gt (PROGNAME.c)"RST" Debug already compiled testBin executable from package with debuggerCmd in config.yml");
    309     putsG(BLD CYN"  -gm (PROGNAME.c)"RST" Debug already compiled memcheckBin executable from package with debuggerCmd in config.yml");
    310     putsG(BLD CYN"  -ga (PROGNAME.c)"RST" Debug already compiled asanBin executable from package with debuggerCmd in config.yml");
    311     putsG(BLD CYN"  -D"RST"               Show default " HOME_DIR CONFIG_NAME " configuration");
    312     putsG(BLD CYN"  -h"RST"               Show this help text");
    313     XSUCCESS
    314   }
    315     }
    316     }
    317     }
    318     }
    319 
    320   if (eqS(argv[1], "-D")) {
    321     terminateG(bin);
    322     puts(defaultSpC);
    323     XSUCCESS
    324   }
    325 
    326   // bin is allocated when there is package.yml and there is only one argmument
    327   // when there is one arguments and the argument is an option, take the program name from package.yml
    328   // when there are 2 arguments and first argument is an option, second argument is the program name
    329   // else run normally argv[1] is main filename
    330   if (eqG(argv[1], "-l")) {
    331     sheepyParams.compileLib  = 1;
    332     dum                      = bin ? ssGet(bin) : argv[2];
    333   }
    334   else if (eqG(argv[1], "-c")) {
    335     sheepyParams.compileOnly = 1;
    336     dum                      = bin ? ssGet(bin) : argv[2];
    337   }
    338   else if (eqG(argv[1], "-e")) {
    339     sheepyParams.showExe     = 1;
    340     dum                      = bin ? ssGet(bin) : argv[2];
    341   }
    342   else if (eqG(argv[1], "-d")) {
    343     sheepyParams.clean       = 1;
    344     dum                      = bin ? ssGet(bin) : argv[2];
    345   }
    346   else if (eqG(argv[1], "-t")) {
    347     testOption:
    348     sheepyParams.test        = 1;
    349     if (bin) {
    350       terminateG(bin);
    351       createSmallJson(packageyml);
    352       readFileG(&packageyml, PACKAGE);
    353       bin = getNDupG(&packageyml, rtSmallStringt, "testBin");
    354       freeG(&packageyml);
    355       dum                      = bin ? ssGet(bin) : argv[2];
    356     }
    357     else {
    358       dum                    = argv[2];
    359   }
    360     }
    361   else if (eqG(argv[1], "-m")) {
    362     memcheckOption:
    363     sheepyParams.memcheck          = 1;
    364     sheepyParams.libsheepyMemcheck = 1;
    365     // check if libsheepyMemcheck exists
    366     dum = catS(libsheepyDir, "/libsheepyMemcheck.a");
    367     if (!fileExists(dum)) {
    368       printf("\n" BLD RED "LibsheepyMemcheck not found: %s not accessible" RST "\nIf libsheepy in another directory, set LIBSHEEPY to libsheepy path, for example: export LIBSHEEPY=/home/$USER/libsheepy/release/\nand compile libsheepyMemcheck with buildMemcheck.sh in the libsheepy git repo\n", dum); {
    369       terminateG(bin);
    370       free(dum);
    371       XFAILURE
    372     }
    373       }
    374     freen(dum);
    375     // select main c file
    376     if (bin) {
    377       terminateG(bin);
    378       createSmallJson(packageyml);
    379       readFileG(&packageyml, PACKAGE);
    380       bin = getNDupG(&packageyml, rtSmallStringt, "memcheckBin");
    381       freeG(&packageyml);
    382       dum                      = bin ? ssGet(bin) : argv[2];
    383     }
    384     else {
    385       dum                    = argv[2];
    386   }
    387     }
    388   else if (eqG(argv[1], "-a")) {
    389     asanOption:
    390     sheepyParams.asan              = 1;
    391     sheepyParams.libsheepyAsan     = 1;
    392     // check if libsheepyMemAsan exists
    393     dum = catS(libsheepyDir, "/libsheepyAsan.a");
    394     if (!fileExists(dum)) {
    395       printf("\n" BLD RED "LibsheepyAsan not found: %s not accessible" RST "\nIf libsheepy in another directory, set LIBSHEEPY to libsheepy path, for example: export LIBSHEEPY=/home/$USER/libsheepy/release/\nand compile libsheepyAsan with buildAsan.sh in the libsheepy git repo\n", dum); {
    396       terminateG(bin);
    397       free(dum);
    398       XFAILURE
    399     }
    400       }
    401     freen(dum);
    402     // select main c file
    403     if (bin) {
    404       terminateG(bin);
    405       createSmallJson(packageyml);
    406       readFileG(&packageyml, PACKAGE);
    407       bin = getNDupG(&packageyml, rtSmallStringt, "asanBin");
    408       freeG(&packageyml);
    409       dum                      = bin ? ssGet(bin) : argv[2];
    410     }
    411     else {
    412       dum                    = argv[2];
    413   }
    414     }
    415   else if (eqG(argv[1], "-n")) {
    416     sheepyParams.new         = 1;
    417     dum                      = argv[2];
    418   }
    419   else if (eqG(argv[1], "-C")) {
    420     sheepyParams.cclass      = 1;
    421     dum                      = argv[2];
    422   }
    423   else if (eqG(argv[1], "-r")) {
    424     sheepyParams.removeBuild = 1;
    425     dum                      = argv[1];
    426   }
    427   else if (eqG(argv[1], "-T")) {
    428     sheepyParams.genTest     = 1;
    429     dum                      = argv[1];
    430   }
    431   else if (eqG(argv[1], "-g")) {
    432     sheepyParams.debugExe    = 1;
    433     dum                      = bin ? ssGet(bin) : argv[2];
    434   }
    435   else if (eqG(argv[1], "-gt")) {
    436     sheepyParams.debugExe    = 1;
    437     goto testOption;
    438   }
    439   else if (eqG(argv[1], "-gm")) {
    440     sheepyParams.debugExe    = 1;
    441     goto memcheckOption;
    442   }
    443   else if (eqG(argv[1], "-ga")) {
    444     sheepyParams.debugExe    = 1;
    445     goto asanOption;
    446   }
    447   else {
    448     dum = argv[1];
    449   }
    450 
    451   if (!dum) {
    452     terminateG(bin);
    453     putsG(BLD RED "Missing program name argument." RST);
    454     XFAILURE
    455   }
    456 
    457 
    458   if (sheepyParams.new) {
    459     terminateG(bin);
    460     rangeFrom(i, 2, argc)
    461       sourceFromTemplate(argv[i]);
    462     XSUCCESS
    463   }
    464 
    465   if (sheepyParams.removeBuild) {
    466     terminateG(bin);
    467     char *build = catS(homedir, "build");
    468     if (fileExists(build)) {
    469       pError0(rmAll(build));
    470       pError0(mkdirParents(build));
    471     }
    472     free(build);
    473     XSUCCESS
    474   }
    475 
    476   if (sheepyParams.genTest) {
    477     terminateG(bin);
    478     if (!fileExists(PACKAGE)) {
    479       putsG(BLD RED PACKAGE " is missing. Test templates not generated." RST);
    480       XFAILURE
    481     }
    482     createSmallJson(pkgInfo);
    483     readFileG(&pkgInfo, PACKAGE);
    484     //logVarG(&pkgInfo);
    485 
    486     // generate testBin
    487     char *testBin = getG(&pkgInfo, rtChar, "testBin");
    488     if (!testBin) {
    489       putsG(BLD RED "testBin is not set in " UDL PACKAGE RST BLD RED ", add a line with '  testBin: test.c'. Test templates not generated." RST);
    490       XFAILURE
    491     }
    492     if (fileExists(testBin)) {
    493       printf(BLD RED "test template %s already exists. Test templates not generated." RST, testBin);
    494       XFAILURE
    495     }
    496     char *s = replaceG(C_TEST_TEMPLATE, "testTemplate", getG(&pkgInfo, rtChar, "name"), 0);
    497     pError0(writeFileG(s, testBin));
    498     free(s);
    499     printf("Generated: "BLD GRN "%s" RST " " UDL "< add the test cases in this file" RST "\n", testBin);
    500     printf("To run the test, execute: " BLD WHT "spm test" RST "\n");
    501     printf(BLD YLW "Warning" RST ": do not call " UDL "initLibsheepy" RST " in " UDL "%s" RST ", it conflicts with libcheck\n", testBin);
    502 
    503     // generate memcheckBin and asanBin
    504     char *memcheckBin = getG(&pkgInfo, rtChar, "memcheckBin");
    505     char *asanBin     = getG(&pkgInfo, rtChar, "asanBin");
    506     if (!memcheckBin and !asanBin) {
    507       putsG(BLD RED "memcheckBin and asanBin are not set in " UDL PACKAGE RST BLD RED ", add a line with '  memcheckBin: testMem.c' or '  asanBin: testMem.c'. Memcheck test templates not generated." RST);
    508       XFAILURE
    509     }
    510 
    511     if (!fileExists(MEMTEST_C_TEMPLATE_NAME)) {
    512       pError0(writeFileG(MEMTEST_C_TEMPLATE, MEMTEST_C_TEMPLATE_NAME));
    513       putsG("Generated: "BLD GRN MEMTEST_C_TEMPLATE_NAME RST);
    514     }
    515     if (!fileExists(RUNMEMTEST_TEMPLATE_NAME)) {
    516       pError0(writeFileG(RUNMEMTEST_TEMPLATE, RUNMEMTEST_TEMPLATE_NAME));
    517       pError0(fileChmod(RUNMEMTEST_TEMPLATE_NAME, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH));
    518       putsG("Generated: "BLD GRN RUNMEMTEST_TEMPLATE_NAME RST);
    519     }
    520     if (memcheckBin) {
    521       char *shell = NULL;
    522       if (isC(testBin)) {
    523         pErrorNULL(setG(testBin, -2, 0));
    524         shell = catS(testBin, "Mem.sh");
    525         // restore original testBin
    526         testBin[strlen(testBin)] = '.';
    527         if (!fileExists(shell)) {
    528           s = replaceManyS(MEMCHECK_SH_TEMPLATE, "testBin", testBin, "memcheckBin", memcheckBin);
    529           pError0(writeFileG(s, shell));
    530           free(s);
    531           pError0(fileChmod(shell, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH));
    532           printf("Generated: "BLD GRN "%s" RST"\n", shell);
    533       }
    534         }
    535       printf("To run the memcheck test, execute: " BLD WHT "%s" RST "\n", shell);
    536       free(shell);
    537     }
    538     if (asanBin) {
    539       char *shell = NULL;
    540       if (isC(testBin)) {
    541         pErrorNULL(setG(testBin, -2, 0));
    542         shell = catS(testBin, "Asan.sh");
    543         // restore original testBin
    544         testBin[strlen(testBin)] = '.';
    545         if (!fileExists(shell)) {
    546           s = replaceManyS(ASAN_SH_TEMPLATE, "testBin", testBin, "asanBin", asanBin);
    547           pError0(writeFileG(s, shell));
    548           free(s);
    549           pError0(fileChmod(shell, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH));
    550           printf("Generated: "BLD GRN "%s" RST"\n", shell);
    551       }
    552         }
    553       printf("To run the asan test, execute: " BLD WHT "%s" RST "\n", shell);
    554       free(shell);
    555     }
    556 
    557     freeG(&pkgInfo);
    558     XSUCCESS
    559   }
    560 
    561   if (sheepyParams.test or sheepyParams.memcheck or sheepyParams.asan) {
    562     sheepyParams.compileOnly = 1;
    563   }
    564 
    565   if (sheepyParams.cclass) {
    566     terminateG(bin);
    567     // make sure first char is lower case
    568     dum[0]   = tolower(dum[0]);
    569 
    570     // generate template filenames
    571     char *fn  = catS(dum, ".h");
    572     char *fn1 = catS(dum, ".c");
    573     char *fn2 = catS(dum, "Internal.h");
    574 
    575     if (fileExists(fn)) {
    576       printf(BLD RED "class file '%s' already exists." RST, fn);
    577       XFAILURE
    578     }
    579     if (fileExists(fn1)) {
    580       printf(BLD RED "class file '%s' already exists." RST, fn1);
    581       XFAILURE
    582     }
    583     if (fileExists(fn2)) {
    584       printf(BLD RED "class file '%s' already exists." RST, fn2);
    585       XFAILURE
    586     }
    587 
    588     char *s  = replaceS(H_CLASS_TEMPLATE, "classTemplate", dum, 0);
    589     char *s1 = replaceS(C_CLASS_TEMPLATE, "classTemplate", dum, 0);
    590     char *s2 = replaceS(INTERNAL_H_CLASS_TEMPLATE, "classTemplate", dum, 0);
    591     dum[0]   = toupper(dum[0]);
    592     pErrorNULL(iReplaceS(&s, "ClassTemplate", dum, 0));
    593     pErrorNULL(iReplaceS(&s1, "ClassTemplate", dum, 0));
    594     pErrorNULL(iReplaceS(&s2, "ClassTemplate", dum, 0));
    595 
    596     char *DUM = upperS(dum);
    597     pErrorNULL(iReplaceS(&s, "CLASSTEMPLATE", DUM, 0));
    598     free(DUM);
    599 
    600     pError0(writeFileG(s, fn));
    601     pError0(writeFileG(s1, fn1));
    602     pError0(writeFileG(s2, fn2));
    603 
    604     dum[0]   = tolower(dum[0]);
    605     printf("Created files for class "BLD GRN"%s"RST":\n%s\n%s\n%s", dum, fn, fn1, fn2); {
    606     freeManyS(s, s1, s2, fn, fn1, fn2);
    607     XSUCCESS
    608   }
    609     }
    610 
    611   if (sheepyParams.libsheepyMemcheck) {
    612     libsheepy = "libsheepyMemcheck.a";
    613   }
    614 
    615   if (sheepyParams.libsheepyAsan) {
    616     libsheepy = "libsheepyAsan.a";
    617   }
    618 
    619   mainFilename = normalizePath(dum);
    620 
    621   sheepyRun:
    622 
    623   if (!fileExists(mainFilename)) {
    624     // add c and cp extension and check if file exists
    625     dum = catS(mainFilename, ".c");
    626     if (fileExists(dum)) {
    627       // add c extension to mainFilename
    628       free(dum);
    629       pErrorNULL(pushG(&mainFilename, ".c"));
    630     }
    631     else {
    632       free(dum);
    633       dum = catS(mainFilename, ".cp");
    634       if (fileExists(dum)) {
    635         // add cp extension to mainFilename
    636         free(dum);
    637         pErrorNULL(pushG(&mainFilename, ".cp"));
    638       }
    639       else {
    640         free(dum);
    641         puts(BLD RED "File not found:" RST);
    642         puts(mainFilename);
    643         XFAILURE
    644   }
    645     }
    646       }
    647 
    648   if (isLink(mainFilename)) {
    649     char *tmp = endlink(mainFilename);
    650     if (!tmp) {
    651       printf(BLD RED "couldn't read link '%s'" RST, mainFilename);
    652       free(mainFilename);
    653       XFAILURE
    654     }
    655     free(mainFilename);
    656     mainFilename = tmp;
    657   }
    658 
    659   // get basename (rootMain)
    660   // mainSrc is main filename without path
    661   // rootMain is the main filename without path and extension
    662   // exePath is the main filename without the extension
    663   // dir is the relative path without the main filename
    664   // actualDir is the real path to main
    665   // sheepy ../bin/example.c p1
    666   //  > mainSrc   = example.c
    667   //  > rootMain  = example
    668   //  > dir       = ../bin
    669   //  > actualDir = /home/remy/bin
    670   char *mainSrc = strdup(basename(mainFilename));
    671   if (isH(mainSrc) || isCH(mainSrc)) {
    672     // print this message only when compiling
    673     if (!sheepyParams.clean) {
    674       puts("No compilation needed for header files.");
    675     } {
    676     XSUCCESS
    677   }
    678       }
    679   if (!isC(mainSrc) && !isCP(mainSrc)) {
    680     // add c and cp extension and check if file exists
    681     dum = catS(mainSrc, ".c");
    682     if (fileExists(dum)) {
    683       // add c extension to mainSrc
    684       free(dum);
    685       pErrorNULL(pushG(&mainSrc, ".c"));
    686     }
    687     else {
    688       free(dum);
    689       dum = catS(mainSrc, ".cp");
    690       if (fileExists(dum)) {
    691         // add cp extension to mainSrc
    692         free(dum);
    693         pErrorNULL(pushG(&mainSrc, ".cp"));
    694       }
    695       else {
    696         free(dum);
    697         puts(BLD RED "Unknown extension. Stop." RST);
    698         puts(mainSrc);
    699         XFAILURE
    700   }
    701     }
    702       }
    703 
    704   char *exePath = strdup(mainFilename);
    705   if (isC(mainSrc)) {
    706     pErrorNULL(setS(exePath, -2, 0));
    707   }
    708   else {
    709     pErrorNULL(setS(exePath, -3, 0));
    710   }
    711 
    712   char *mainH       = strdup(mainSrc);
    713   pErrorNULL(setS(mainH, -1, 'h'));
    714 
    715   // [0] root name [1] extension
    716   char **rootMain_l = split(mainSrc, ".");
    717   char *rootMain    = rootMain_l[0];
    718 
    719   // main source folder (dir)
    720   // Ex: dir = ../bin or ./ when there is no folder in mainFilename
    721   char *dir = shDirname(mainFilename);
    722 
    723   // if there is package.yml in dir, check if there are package dependencies and the shpPackages folder
    724   // install the packages when shpPackages is not found
    725   dum = catS(dir, "/"PACKAGE_DIR);
    726   pErrorNULL(normalizePathG(&dum));
    727   if (!sheepyParams.clean && not isDir(dum)) {
    728     free(dum);
    729     dum = catS(dir, "/", PACKAGE);
    730     pErrorNULL(normalizePathG(&dum));
    731     if (fileExists(dum)) {
    732       createSmallJson(pkgInfo);
    733       readFileG(&pkgInfo, dum);
    734       smallDictt *pkgDeps = getG(&pkgInfo, rtSmallDictt, "dependencies");
    735       if (pkgDeps) {
    736         if (lenG(pkgDeps)) {
    737           char *c = formatS("cd %s ; spm install", dir);
    738           if (getG(cfgD, rtBool, "print_compile_commands")) {
    739             printf("%s\n", c);
    740           }
    741           programResult |= commandNFree(c);;
    742         }
    743         finishG(pkgDeps);
    744       }
    745       freeG(&pkgInfo);
    746     }
    747     free(dum);
    748   }
    749   else {
    750     free(dum);
    751   }
    752 
    753   // find out real path for main source
    754   char actualpath[PATH_MAX+1];
    755   char *actualDir;
    756   realpath(mainFilename, actualpath);
    757   actualDir = shDirname(actualpath);
    758   //logVarG(actualDir);
    759 
    760   char *buildPath;
    761 
    762   if (sheepyParams.test) {
    763     buildPath = catS(homedir, "build", actualDir, "Test");
    764   }
    765   else if (sheepyParams.memcheck) {
    766     buildPath = catS(homedir, "build", actualDir, "Memcheck");
    767   }
    768   else if (sheepyParams.asan) {
    769     buildPath = catS(homedir, "build", actualDir, "Asan");
    770   }
    771   else {
    772     // compile, run or clean
    773     buildPath = catS(homedir, "build", actualDir);
    774     if (sheepyParams.clean) {
    775       // delete test build folders: test, memcheck...
    776       dum = catS(homedir, "build", actualDir, "Test");
    777       if (isDir(dum)) {
    778         if (getG(cfgD, rtBool, "print_compile_commands")) {
    779           printf("rm -rf %s", dum);
    780           printf("\n");
    781         }
    782         pError0(rmAll(dum));
    783       }
    784       free(dum);
    785       dum = catS(homedir, "build", actualDir, "Memcheck");
    786       if (isDir(dum)) {
    787         if (getG(cfgD, rtBool, "print_compile_commands")) {
    788           printf("rm -rf %s", dum);
    789           printf("\n");
    790         }
    791         pError0(rmAll(dum));
    792       }
    793       free(dum);
    794       dum = catS(homedir, "build", actualDir, "Asan");
    795       if (isDir(dum)) {
    796         if (getG(cfgD, rtBool, "print_compile_commands")) {
    797           printf("rm -rf %s", dum);
    798           printf("\n");
    799         }
    800         pError0(rmAll(dum));
    801       }
    802       free(dum);
    803   }
    804     }
    805 
    806 
    807   char *buildExePath = catS(buildPath, "/", rootMain);
    808   //logVarG(buildPath);
    809 
    810 
    811   if (sheepyParams.debugExe) {
    812     char *debuggerCmd = getG(cfgJ, rtChar, "debuggerCmd");;
    813     if (!debuggerCmd) {
    814       printf(BLD RED"debuggerCmd not found in "HOME_DIR CONFIG_NAME ", run 'sheepy -D' to see an example." RST);
    815       XFailure;
    816     }
    817     if (!fileExists(buildExePath)) {
    818       printf(BLD RED"Executable not found, run the compile command first. Path was: %s"RST, buildExePath);
    819       XFailure;
    820     }
    821 
    822     createAllocateSmallArray(paramL);
    823     // debug program with parameters on command line
    824     rangeFrom(i, 3, argc)
    825       pErrorNULL(pushG(paramL, argv[i]));
    826     // create parameter string for running the program in argv[1]
    827     // add quotes to keep the same number of arguments
    828     char *params = joinSG(paramL, "\" \"");
    829     if (!params) {
    830       emptyS(params);
    831     }
    832     else {
    833       pErrorNULL(prependG(&params, "\""));
    834       pErrorNULL(pushG(&params, "\""));
    835     }
    836 
    837     char *debugCommand = formatS("%s %s %s", debuggerCmd, buildExePath, params);;
    838     pErrorNot0(system(debugCommand));
    839     if (getG(cfgD, rtBool, "print_compile_commands")) {
    840       printf("%s\n", debugCommand);
    841     }
    842     XSuccess;
    843   }
    844 
    845 
    846   if (sheepyParams.showExe) {
    847     printf("The executable path is:      " GRN "%s" RST
    848          "\nThe executable directory is: " GRN "%s" RST, buildExePath, shDirname(buildExePath));
    849     XSUCCESS
    850   }
    851   if (!fileExists(buildPath)) {
    852     pError0(mkdirParents(buildPath));
    853   }
    854 
    855   // load main source (mainCode)
    856   // Steps
    857     // detect hash bang and remove
    858     // find dependencies
    859     // recursively search dependencies
    860     // search for other includes
    861   createAllocateSmallArray(mainFile);
    862   readFileG(mainFile, mainFilename);
    863 
    864     // detect hash bang and remove
    865   char *hBang = NULL;
    866   hBang = getG(mainFile, hBang, 0);
    867   if (hBang && hBang[0] == '#' && hBang[1] == '!' && findS(hBang, "sheepy")) {
    868     pErrorNULL(setG(mainFile, 0, ""));
    869   }
    870 
    871   // if main is cp, convert to c in buildpath
    872   smallArrayt *cCode;
    873   if (isC(mainSrc)) {
    874     cCode = dupG(mainFile);
    875   }
    876   else {
    877     // main is cp, convert to c in buildpath
    878     // generate c
    879     initiateG(&cCode);
    880     generateCBuffers(mainFile, cCode);
    881   }
    882     /* forEachSmallArray(mainFile, lO) */
    883     /*   // convert ch include to h */
    884     /*   castS(l, duplicateG(lO)) */
    885     /*   if hasG(l, ".ch\"") */
    886     /*     replaceG(l, ".ch\"", ".h\"", 0); */
    887     /*   pushNFreeG(cCode, l); */
    888     /*   free lO */
    889 
    890   // create generated main path and save the main c code in it
    891   char *mainCPath = catS(buildPath, "/", mainSrc);
    892   if (isCP(mainSrc)) {
    893     // cp file
    894     pErrorNULL(setS(mainCPath, -2, 'c'));
    895     pErrorNULL(setS(mainCPath, -1, 0));
    896   }
    897   writeTextO(cCode, mainCPath);
    898   terminateG(cCode);
    899 
    900     // find dependencies
    901     // recursively search dependencies
    902     // TODO remove/ignore comments before searching for includes
    903     // inc is the list of all includes from all h and c files
    904     // the c files associated with the includes are compiled
    905     // create dependency dictionary
    906   createAllocateSmallDict(dependencies);
    907   // dependencies dict:
    908   // keys:   h files
    909   // values: first level header dependencies
    910   createAllocateSmallArray(inc);
    911   // TODO check if the h is already in dependencies
    912   createAllocateSmallArray(mainDepFiles);
    913   iter(mainFile, l) {
    914     castS(L, l)
    915     // TODO improve main function detection
    916     if (hasG(L, "main") && (hasG(L, "int") || hasG(L, "define")) && !sheepyParams.compileLib) {
    917       // main function detected
    918       // compile the executable if requested
    919       sheepyParams.dontCompileExe = 0;
    920     }
    921     char *s = getInclude(L);
    922     if (s) {
    923       // check if c includes itself
    924       char *fN;
    925       fN = strdup(basename(s));
    926       // add new include to inc array
    927       char *realFPath  = catS(actualDir, "/", s);
    928       char *buildFPath = catS(buildPath, "/", s);
    929       pErrorNULL(iNormalizePath(&realFPath));
    930       pErrorNULL(iNormalizePath(&buildFPath));
    931       // create a link to header in buildPath
    932       // because main.c is in buildPath and the compiler needs
    933       // to find the headers
    934       if (symlinkFile(realFPath, buildFPath)) {
    935         pErrorNULL(pushG(inc, s));
    936         pErrorNULL(pushG(mainDepFiles, s));
    937       }
    938       freeManyS(realFPath, buildFPath);
    939       free(fN);
    940       free(s);
    941     }
    942   }
    943   terminateG(mainFile);
    944 
    945   // add mainFilename.h to dependencies for cases when mainFilename doesn't include the header
    946   // (this handles empty main source files)
    947   if (isC(mainFilename)) {
    948     pErrorNULL(setG(mainFilename, -1, 'h'));
    949     if (fileExistsG(mainFilename) && !hasG(inc, mainH)) {
    950       pErrorNULL(pushG(inc, mainH));
    951       pErrorNULL(pushG(mainDepFiles, mainH));
    952     }
    953     pErrorNULL(setG(mainFilename, -1, 'c'));
    954   }
    955 
    956   setNFreeG(dependencies, mainSrc, mainDepFiles);
    957   //logVarG(dependencies);
    958   //print '**** inc ****'
    959   //logVarG(inc);
    960 
    961     // search for other includes
    962   forEachSmallArray(inc, otherIncs) {
    963     castS(L, dupG(otherIncs))
    964     //logVarG(inc);
    965     //logVarG(dependencies);
    966 
    967     createAllocateSmallArray(file);
    968     char *fileDir  = shDirname(ssGet(L));
    969     char *fileName;
    970     fileName = strdup(basename(ssGet(L)));
    971 
    972 
    973     if (hasG(L, PACKAGE_DIR)) {
    974       // this include belongs to a package
    975       // the package are already compiled
    976       // stop searching for further includes
    977       // check if the h is already in dependencies, process only new found sources
    978       if (!hasG(dependencies, ssGet(L))) {
    979         // scan the package header to symlink includes in the build path so that the compiler find them
    980         // real path to h file
    981         dum = catS(actualDir, "/", ssGet(L));
    982         readFileG(file, dum);
    983         free(dum);
    984 
    985         iter(file, hl) {
    986         castS(hL, hl)
    987           char *s = getInclude(hL);
    988           if (s) {
    989             // check if c includes itself
    990             char *fN;
    991             fN = strdup(basename(s));
    992             // !hasG(inc, s): add to inc only new files to avoid infinite loops
    993             if (!eqG(fN, fileName) && !hasG(inc, s)) {
    994               // add new include to inc array
    995               char *tmp        = catS(fileDir, "/", s);
    996               pErrorNULL(iNormalizePath(&tmp));
    997               char *realFPath  = catS(actualDir, "/", tmp);
    998               char *buildFPath = catS(buildPath, "/", tmp);
    999               pErrorNULL(iNormalizePath(&realFPath));
   1000               pErrorNULL(iNormalizePath(&buildFPath));
   1001               //logVarG(fileDir);
   1002               //logVarG(s);
   1003               //logVarG(realFPath);
   1004               // create a link to header in buildPath
   1005               // because main.c is in buildPath and the compiler needs
   1006               // to find the headers
   1007               symlinkFile(realFPath, buildFPath);
   1008               freeManyS(tmp, realFPath, buildFPath);
   1009             }
   1010             free(fN);
   1011             free(s);
   1012           }
   1013         }
   1014         freeG(file);
   1015 
   1016         // add the a libs to detect when the package dependencies are updated and recompile the source files
   1017         createAllocateSmallArray(libDep);
   1018         // TODO support ch and cp
   1019         // remove sub directories in packages, the a file is located at the package root
   1020         // shpPackages/ini/src/ini.h
   1021         // shpPackages/ini/ini.a
   1022         smallArrayt *spl    = splitG(L, PACKAGE_DIR"/");
   1023         smallStringt *last  = getG(spl, rtSmallStringt, -1);
   1024         pErrorNULL(setG(last, -1, 'a'));
   1025         // last is "ini/src/ini.a"
   1026         smallArrayt *sslash = splitG(last, '/');
   1027         finishG(last);
   1028         char *lib           = catS(getG(sslash, rtChar, 0), "/", getG(sslash, rtChar, -1));
   1029         setNFreeG(spl, -1, lib);
   1030         lib                 = joinSG(spl, PACKAGE_DIR"/");
   1031         terminateManyG(spl,sslash);
   1032         if (fileExists(lib)) {
   1033           // link library only when it exists, there is no library in header only packages
   1034           pushNFreeG(libDep, lib);
   1035         }
   1036         else {
   1037           free(lib);
   1038         }
   1039         pErrorNULL(setG(L, -1, 'h'));
   1040         setNFreeG(dependencies, ssGet(L), libDep);
   1041       }
   1042       terminateG(L);
   1043       finishG(otherIncs);
   1044       continue;
   1045     }
   1046 
   1047     smallArrayt *depFiles = NULL;
   1048     // check if the h is already in dependencies, process only new found sources
   1049     if (!hasG(dependencies, ssGet(L))) {
   1050       initiateAllocateSmallArray(&depFiles);
   1051 
   1052       // scan h
   1053       // real path to h file
   1054       dum = catS(actualDir, "/", ssGet(L));
   1055       readFileG(file, dum);
   1056       free(dum);
   1057 
   1058       forEachSmallArray(file, hl) {
   1059         castS(hL, hl)
   1060         char *s = getInclude(hL);
   1061         if (s) {
   1062           // check if c includes itself
   1063           char *fN;
   1064           fN = strdup(basename(s));
   1065           if (!eqG(fN, fileName)) {
   1066             // add new include to inc array
   1067             char *tmp        = catS(fileDir, "/", s);
   1068             pErrorNULL(iNormalizePath(&tmp));
   1069             char *realFPath  = catS(actualDir, "/", tmp);
   1070             char *buildFPath = catS(buildPath, "/", tmp);
   1071             pErrorNULL(iNormalizePath(&realFPath));
   1072             pErrorNULL(iNormalizePath(&buildFPath));
   1073             //logVarG(fileDir);
   1074             //logVarG(s);
   1075             //logVarG(realFPath);
   1076             // create a link to header in buildPath
   1077             // because main.c is in buildPath and the compiler needs
   1078             // to find the headers
   1079             if (symlinkFile(realFPath, buildFPath)) {
   1080               // !hasG(inc, s): add to inc only new files to avoid infinite loops
   1081               if (!hasG(inc, s)) {
   1082                 pErrorNULL(pushG(inc, tmp));
   1083               }
   1084               pErrorNULL(pushG(depFiles, tmp));
   1085             }
   1086             freeManyS(tmp, realFPath, buildFPath);
   1087           }
   1088           free(fN);
   1089           free(s);
   1090         }
   1091         finishG(hL);
   1092       }
   1093 
   1094       pErrorNULL(normalizePathO(L));
   1095       setNFreeG(dependencies, ssGet(L), depFiles);
   1096     }
   1097 
   1098     // scan c or cp
   1099     freeG(file);
   1100     if (isH(fileName)) {
   1101       pErrorNULL(setG(L, -1, 'c'));
   1102     }
   1103     else {
   1104       // scan .cp
   1105       pErrorNULL(setG(L, -1, 'p'));
   1106     }
   1107 
   1108     // check if the c is already in dependencies, process only new found sources
   1109     if (!hasG(dependencies, ssGet(L))) {
   1110       initiateAllocateSmallArray(&depFiles);
   1111 
   1112       dum = catS(actualDir, "/", ssGet(L));
   1113       if (fileExists(dum)) {
   1114         readFileG(file, dum);
   1115 
   1116         forEachSmallArray(file, cl) {
   1117           castS(cL, cl)
   1118           char *s = getInclude(cL);
   1119           // !hasG(inc, s): add to inc only new files to avoid infinite loops
   1120           if (s) {
   1121             char *tmp = catS(fileDir, "/", s);
   1122             pErrorNULL(iNormalizePath(&tmp));
   1123             char *realFPath  = catS(actualDir, "/", tmp);
   1124             char *buildFPath = catS(buildPath, "/", tmp);
   1125             pErrorNULL(iNormalizePath(&realFPath));
   1126             pErrorNULL(iNormalizePath(&buildFPath));
   1127             if (symlinkFile(realFPath, buildFPath)) {
   1128               pErrorNULL(pushG(depFiles, tmp));
   1129               // check if c includes itself
   1130               char *fN;
   1131               fN = strdup(basename(s));
   1132               if (!eqG(fN, fileName) && !hasG(inc, s)) {
   1133                 // add new include to inc array
   1134                 pErrorNULL(pushG(inc, tmp));
   1135               }
   1136               free(fN);
   1137             }
   1138             freeManyS(tmp, s, realFPath, buildFPath);
   1139           }
   1140           finishG(cL);
   1141         }
   1142 
   1143         setNFreeG(dependencies, ssGet(L), depFiles);
   1144       }
   1145 
   1146       free(dum);
   1147     }
   1148     freeManyS(fileName, fileDir, otherIncs);
   1149     terminateManyG(file, L);
   1150   }
   1151 
   1152   //logVarG(dependencies);
   1153   //print '**** inc ****'
   1154   //logVarG(inc);
   1155 
   1156   // uniquify inc list
   1157   // to avoid compiling and linking several times the same file
   1158   enumerateSmallArray(inc, hE, i) {
   1159     castS(h, hE)
   1160     pErrorNULL(normalizePathO(h));
   1161     setNFreePG(inc, i, h);
   1162   }
   1163   uniqG(inc, unusedV);
   1164 
   1165   //logVarG(inc);
   1166 
   1167   // parameters
   1168   createAllocateSmallArray(paramL);
   1169 
   1170   // run program with parameters on command line
   1171   // when argv[1] is an option (-l, -c ...) the program is not
   1172   // executed
   1173   rangeFrom(i, 2, argc)
   1174     pErrorNULL(pushG(paramL, argv[i]));
   1175 
   1176   // create parameter string for running the program in argv[1]
   1177   // add quotes to keep the same number of arguments
   1178   char *params = joinSG(paramL, "\" \"");
   1179   if (!params) {
   1180     emptyS(params);
   1181   }
   1182   else {
   1183     pErrorNULL(prependG(&params, "\""));
   1184     pErrorNULL(pushG(&params, "\""));
   1185   }
   1186 
   1187   // detect execute bit
   1188   // if yes then run (run)
   1189   char *run = emptySF();
   1190 
   1191   if (!sheepyParams.compileLib && !sheepyParams.compileOnly && access(mainFilename, X_OK) == 0) {
   1192     // run
   1193     // -l is not in the parameters
   1194     pErrorNULL(iAppendManyS(&run, "; ", buildExePath, " ", params));
   1195   }
   1196   else if (!sheepyParams.clean) {
   1197     printf(BLD BLU "Compile only" RST " %s\n", rootMain);
   1198   }
   1199 
   1200   // compile options
   1201   char *m32bit;
   1202   char *ar32bit;
   1203   if (getG(cfgD, rtBool, "32bit")) {
   1204     m32bit  = "-m32";
   1205     ar32bit = "--target=elf32-i386";
   1206   }
   1207   else {
   1208     m32bit  = "";
   1209     ar32bit = "";
   1210   }
   1211 
   1212   //logVarG(dependencies);
   1213     // check updates in all dependencies
   1214   // create cDependencies dict
   1215   // key:   c files
   1216   // value: array of direct/indirect includes
   1217   //logP("--- cDependencies");
   1218   createAllocateSmallDict(cDependencies);
   1219   if (lenG(dependencies) > 0) {
   1220     char *k;
   1221     iter(dependencies, depArray) {
   1222       k = (char*)iterKeyG(dependencies);
   1223       if (isC(k) || isCP(k)) {
   1224         //logVarG(k);
   1225         cast(smallArrayt *, depsOrig, depArray);
   1226         // add dependencies from header file to c dependency list
   1227         // to recompile when a header is updated
   1228         if (isC(k)) {
   1229           pErrorNULL(setS(k, -1, 'h'));
   1230           if (hasG(dependencies, k)) {
   1231             pErrorNULL(appendNFreeG(depsOrig, getNDupG(dependencies, rtSmallArrayt, k)));
   1232           }
   1233           pErrorNULL(setS(k, -1, 'c'));
   1234         }
   1235         elif (isCP(k)) {
   1236           pErrorNULL(setS(k, -1, 'h'));
   1237           if (hasG(dependencies, k)) {
   1238             pErrorNULL(appendNFreeG(depsOrig, getNDupG(dependencies, rtSmallArrayt, k)));
   1239           }
   1240           pErrorNULL(setS(k, -1, 'p'));
   1241         }
   1242         // update depArray pointer in dependencies because it might have been updated
   1243         setPG(dependencies, k, depsOrig);
   1244         smallArrayt *deps = dupG(depsOrig);
   1245         // recursively add dependencies for the C file 'k'
   1246         createAllocateSmallArray(depStack);
   1247         pErrorNULL(pushG(depStack, depsOrig));
   1248         while (not isEmptyG(depStack)) {
   1249           smallArrayt *fileDeps = dequeueG(depStack, rtSmallArrayt);;
   1250           // for each dep, append dep array to c dep array
   1251           forEachSmallArray(fileDeps, dep) {
   1252             castS(depName, dep)
   1253             if (isH(ssGet(depName)) || isCH(ssGet(depName))) {
   1254               // check if the array is empty
   1255               smallArrayt *dp       = getG(dependencies, rtSmallArrayt, ssGet(depName));
   1256               if (dp and not isEmptyG(dp)) {
   1257                 pErrorNULL(pushG(depStack, dp));
   1258                 smallArrayt *thisDeps = getNDupG(dependencies, rtSmallArrayt, ssGet(depName));
   1259                 pErrorNULL(appendNSmashG(deps, thisDeps));
   1260               }
   1261               finishG(dp);
   1262             }
   1263             finishG(dep);
   1264           }
   1265           finishG(fileDeps);
   1266         }
   1267         terminateG(depStack);
   1268         // uniquify deps
   1269         uniqG(deps, unusedV);
   1270         // add array to cDependencies
   1271         setNFreeG(cDependencies, k, deps);
   1272       }
   1273     }
   1274   }
   1275 
   1276   terminateG(dependencies);
   1277 
   1278   //logVarG(cDependencies);
   1279 
   1280   createAllocateSmallDict(toCompile);
   1281 
   1282   // check if h and c (not all of them are in the list) files are modified
   1283   if (lenG(cDependencies) > 0) {
   1284     const char *k;
   1285     createSmallArray(copyAllHeaderDatesToDep);
   1286     // Array to store h deps to be able to copy deps after all dependencies are found
   1287     createSmallArray(copyHeaderDepToBuildPath);
   1288     iter(cDependencies, depValue) {
   1289       k = iterKeyG(cDependencies);
   1290       //logVarG(k);
   1291       cast(smallArrayt *, hDeps, depValue)
   1292 
   1293       char *realCPath  = catS(actualDir, "/", k);
   1294       char *buildCPath = catS(buildPath, "/", k);
   1295       // TODO free ^^ before continue and at the end
   1296       pErrorNULL(iNormalizePath(&realCPath));
   1297       pErrorNULL(iNormalizePath(&buildCPath));
   1298       // detect c file is updated
   1299       // get modification time for c file k
   1300       struct stat cst;
   1301       pError(stat(realCPath, &cst))
   1302       free(realCPath);
   1303 
   1304       // check if object already exists in build path
   1305       if (isC(k)) {
   1306         pErrorNULL(setS(buildCPath, -1, 'o'));
   1307       }
   1308       else {
   1309         pErrorNULL(setS(buildCPath, -2, 'o'));
   1310         pErrorNULL(setS(buildCPath, -1, 0));
   1311       }
   1312       //puts(buildCPath);
   1313       if (fileExists(buildCPath)) {
   1314         //logP("EXISTS");
   1315 
   1316         struct stat ost;
   1317         pError(stat(buildCPath, &ost))
   1318         //logVarG(cst.st_mtime);
   1319         //logVarG(ost.st_mtime);
   1320         if (cst.st_mtime != ost.st_mtime or sheepyParams.clean) {
   1321           // when cleaning, add all C files
   1322           //logP("DIFFERENT DATES RECOMPILE");
   1323           addFileToCompile(actualDir, k, buildCPath, toCompile);
   1324           // copy header dates to not recompile unecesserarily next time
   1325           // change dep files after all modified files are found
   1326           createSmallArray(headerInfo);
   1327           pErrorNULL(pushG     (&headerInfo, actualDir));
   1328           pErrorNULL(pushNFreeG(&headerInfo, dupG(hDeps)));
   1329           pErrorNULL(pushG     (&headerInfo, buildPath));
   1330           pErrorNULL(pushG     (&copyAllHeaderDatesToDep, &headerInfo));
   1331           free(buildCPath);
   1332           continue;
   1333         }
   1334         else {
   1335           // logP("C AND O HAVE SAME DATE - CHECK IF H FILES ARE UPDATED");
   1336           // check if h file for this c file is modified
   1337           // if not check if h files dependencies are modified
   1338           // execute the code below
   1339 
   1340           //logP("CHECK HEADER");
   1341           // get modification time for header
   1342           struct stat hst;
   1343           dum = catS(actualDir, "/", k);
   1344           pErrorNULL(setS(dum, -1, 'h'));
   1345           if (fileExists(dum)) {
   1346             pError(stat(dum, &hst))
   1347             free(dum);
   1348 
   1349             // get modification time for dep file
   1350             dum = catS(buildPath, "/", k, ".dep");
   1351             pErrorNULL(setS(dum, -5, 'h'));
   1352             //logVarG(k);
   1353             //logVarG(dum);
   1354             if (fileExists(dum)) {
   1355               //puts("H EXISTS");
   1356               // compare to header modification time in build path
   1357               struct stat bst;
   1358               pError(stat(dum, &bst))
   1359               if (hst.st_mtime != bst.st_mtime) {
   1360                 // h file for the k c file is modified, recompile the c file
   1361                 addFileToCompile(actualDir, k, buildCPath, toCompile);
   1362                 // copy header dates to not recompile unecesserarily next time
   1363                 // change dep files after all modified files are found
   1364                 createSmallArray(headerInfo);
   1365                 pErrorNULL(pushG     (&headerInfo, actualDir));
   1366                 pErrorNULL(pushNFreeG(&headerInfo, dupG(hDeps)));
   1367                 pErrorNULL(pushG     (&headerInfo, buildPath));
   1368                 pErrorNULL(pushG     (&copyAllHeaderDatesToDep, &headerInfo));
   1369                 free(buildCPath);
   1370                 free(dum);
   1371                 continue;
   1372           }
   1373             }
   1374               }
   1375           free(dum);
   1376       }
   1377         }
   1378       else {
   1379         // object file not found, compile c file
   1380         //logP("COMPILE");
   1381         //puts(buildCPath);
   1382         addFileToCompile(actualDir, k, buildCPath, toCompile);
   1383         // copy header dates to not recompile unecesserarily next time
   1384         // change dep files after all modified files are found
   1385         createSmallArray(headerInfo);
   1386         pErrorNULL(pushG     (&headerInfo, actualDir));
   1387         pErrorNULL(pushNFreeG(&headerInfo, dupG(hDeps)));
   1388         pErrorNULL(pushG     (&headerInfo, buildPath));
   1389         pErrorNULL(pushG     (&copyAllHeaderDatesToDep, &headerInfo));
   1390         free(buildCPath);
   1391         continue;
   1392       }
   1393 
   1394       bool addCFileK = no;
   1395       // check if h files are modified
   1396       forEachSmallArray(hDeps, dep) {
   1397         castS(Dep, dep);
   1398         char *depName = ssGet(Dep);
   1399 
   1400         // get modification time for header depName
   1401         struct stat hst;
   1402         dum = catS(actualDir, "/", depName);
   1403         pError(stat(dum, &hst))
   1404         free(dum);
   1405 
   1406         dum = catS(buildPath, "/", depName, ".dep");
   1407         //logVarG(depName);
   1408         //logVarG(dum);
   1409         if (fileExists(dum)) {
   1410           //logP("H EXISTS");
   1411           // compare to header modification time in build path
   1412           struct stat bst;
   1413           pError(stat(dum, &bst))
   1414           if (hst.st_mtime != bst.st_mtime or sheepyParams.clean) {
   1415             // clean all files
   1416             // header is updated
   1417             //logP("H UPDATED");
   1418             addCFileK = yes;
   1419             char *hBP = catS(buildPath, "/", depName);
   1420             // store h dep in array to copy deps after all dependencies are found
   1421             storeHeaderDep(&copyHeaderDepToBuildPath, hBP, hst);
   1422             addFileToCompile(actualDir, k, buildCPath, toCompile);
   1423 
   1424             // when h file is modified, recompile the associated C/CP file
   1425             if (!hasG(depName, PACKAGE_DIR)) {
   1426                 char *cfile = dupG(depName);
   1427                 if (isH(cfile)) {
   1428                   pErrorNULL(setG(cfile, -1, 'c'));
   1429                 }
   1430                 else {
   1431                   pErrorNULL(setG(cfile, -1, 'p'));
   1432                 }
   1433                 if (fileExists(cfile)) {
   1434                   char *buildCPathForH = catS(buildPath, "/", cfile);
   1435                   if (isC(k)) {
   1436                     pErrorNULL(setS(buildCPathForH, -1, 'o'));
   1437                   }
   1438                   else {
   1439                     pErrorNULL(setS(buildCPathForH, -2, 'o'));
   1440                     pErrorNULL(setS(buildCPathForH, -1, 0));
   1441                   }
   1442                   addFileToCompile(actualDir, cfile, buildCPathForH, toCompile);
   1443                   free(buildCPathForH);
   1444                 }
   1445                 free(cfile);
   1446           }
   1447             }
   1448           else if (endsWithG(depName, ".a")) {
   1449             // check if package is newer than executable or library
   1450             // to handle case when several program/libraries use a common package
   1451             // (for example: 2 programs in the directory using a common package)
   1452             if (!sheepyParams.compileLib) {
   1453               // check buildExePath
   1454               if (fileExists(buildExePath)) {
   1455                 struct stat bst;
   1456                 pError(stat(buildExePath, &bst))
   1457                 if (bst.st_mtime < hst.st_mtime) {
   1458                   // recompile source file k because the existing exe is older than the depName lib
   1459                   if (!sheepyParams.clean) {
   1460                     logI("recompile source file '%s' because the existing exe is older than %s", k, depName);
   1461                   }
   1462                   char *hBP = catS(buildPath, "/", depName);
   1463                   // store h dep in array to copy deps after all dependencies are found
   1464                   storeHeaderDep(&copyHeaderDepToBuildPath, hBP, hst);
   1465                   addFileToCompile(actualDir, k, buildCPath, toCompile);
   1466             }
   1467               }
   1468                 }
   1469             else {
   1470               // check if package is newer than the library currently being built
   1471               char *libName = catS(rootMain, ".a");
   1472               if (fileExists(libName)) {
   1473                 struct stat bst;
   1474                 pError(stat(libName, &bst))
   1475                 if (bst.st_mtime < hst.st_mtime) {
   1476                   // recompile source file k because the existing exe is older than the depName lib
   1477                   if (!sheepyParams.clean) {
   1478                     logI("recompile source file '%s' because the existing exe is older than %s", k, depName);
   1479                   }
   1480                   char *hBP = catS(buildPath, "/", depName);
   1481                   // store h dep in array to copy deps after all dependencies are found
   1482                   storeHeaderDep(&copyHeaderDepToBuildPath, hBP, hst);
   1483                   addFileToCompile(actualDir, k, buildCPath, toCompile);
   1484               }
   1485                 }
   1486               free(libName);
   1487         }
   1488           }
   1489             }
   1490         else {
   1491           // new h file: compile dependencies and copy h file to build path
   1492           char *hBP = catS(buildPath, "/", depName);
   1493           // store h dep in array to copy deps after all dependencies are found
   1494           storeHeaderDep(&copyHeaderDepToBuildPath, hBP, hst);
   1495           addFileToCompile(actualDir, k, buildCPath, toCompile);
   1496         }
   1497 
   1498         free(dum);
   1499         finishG(dep);
   1500       }
   1501       if (addCFileK) {
   1502         addFileToCompile(actualDir, k, buildCPath, toCompile);
   1503       }
   1504       free(buildCPath);
   1505       //logVarG(toCompile);
   1506     }
   1507     // copy all header dates to dep files in build directory
   1508     iter(&copyAllHeaderDatesToDep, HeaderInfo) {
   1509       cast(smallArrayt*, headerInfo, HeaderInfo);
   1510       copyHeaderDates(/*actualDir*/getG(headerInfo, rtChar, 0), /*hDeps*/getG(headerInfo, rtSmallArrayt, 1), /*buildPath*/getG(headerInfo, rtChar, 2));
   1511     }
   1512     iter(&copyHeaderDepToBuildPath, HeaderInfo) {
   1513       cast(smallContainert*,headerInfo, HeaderInfo);
   1514       srcFilet *srcI = O(headerInfo,get);;
   1515       copyHeaderToBuildPath(srcI->bpath, srcI->st);
   1516       free(srcI->bpath);
   1517       free(srcI);
   1518     }
   1519     freeG(&copyAllHeaderDatesToDep);
   1520   }
   1521 
   1522   // add all c file to compile list
   1523   if (lenG(inc) > 0) {
   1524     smallArrayt *hList = duplicateG(inc);
   1525     forEachSmallArray(hList, incI) {
   1526       castS(L, incI)
   1527 
   1528       if (hasG(L, PACKAGE_DIR)) {
   1529         // the packages are already compiled
   1530         // dont need to compile corresponding c file
   1531         finishG(incI);
   1532         continue;
   1533       }
   1534 
   1535       if (isH(ssGet(L))) {
   1536         pErrorNULL(setG(L, -1, 'c'));
   1537       }
   1538       else {
   1539         pErrorNULL(setG(L, -1, 'p'));
   1540       }
   1541 
   1542       if (!fileExistsO(L)) {
   1543         // no corresponding c file
   1544         // skip
   1545         finishG(incI);
   1546         continue;
   1547       }
   1548 
   1549       // create object path
   1550       char *oPath;
   1551 
   1552       if (isC(ssGet(L))) {
   1553         pErrorNULL(setG(L, -1, 'o'));
   1554         oPath = catS(buildPath, "/", ssGet(L));
   1555         // restore c extension
   1556         pErrorNULL(setG(L, -1, 'c'));
   1557       }
   1558       else {
   1559         char *tmp = toStringG(L);
   1560         pErrorNULL(setS(tmp, -2, 'o'));
   1561         pErrorNULL(setS(tmp, -1, 0));
   1562         oPath = catS(buildPath, "/", tmp);
   1563         free(tmp);
   1564       }
   1565 
   1566 
   1567       // compare c and o file dates
   1568       // get modification time for c file L
   1569       struct stat cst;
   1570       pError(stat(ssGet(L), &cst))
   1571 
   1572       //putsG(L);
   1573       //puts(oPath);
   1574       if (fileExists(oPath)) {
   1575         //puts("EXISTS");
   1576 
   1577         struct stat ost;
   1578         pError(stat(oPath, &ost))
   1579         if (cst.st_mtime != ost.st_mtime or sheepyParams.clean) {
   1580           // clean all files
   1581           //puts("DIFFERENT DATES RECOMPILE");
   1582           addFileToCompile(actualDir, ssGet(L), oPath, toCompile);
   1583         }
   1584         //else
   1585         //  puts("C AND O HAVE SAME DATE SKIP COMPILATION");
   1586       }
   1587       else {
   1588         // object file not found, compile c file
   1589         //puts("COMPILE");
   1590         //puts(oPath);
   1591         addFileToCompile(actualDir, ssGet(L), oPath, toCompile);
   1592       }
   1593 
   1594       free(oPath);
   1595       finishG(incI);
   1596     }
   1597     terminateG(hList);
   1598   }
   1599 
   1600   //logVarG(toCompile);
   1601 
   1602   char **sourcesToCompile = keysG(toCompile);
   1603 
   1604   // TODO search index of mainSrc, delete from sourcesToCompile and remove if eqG(*src, mainSrc)
   1605 
   1606   // generate the h and c code from sheepy files
   1607   if (lenG(toCompile) > 0) {
   1608     forEachCharP(sourcesToCompile, src) {
   1609       if (eqG(*src, mainSrc)) {
   1610         // main c file is compiled seperately to take care of the hashbang
   1611         continue;
   1612       }
   1613       if (isCP(*src)) {
   1614         // convert cp to c
   1615         char *bpath = catS(buildPath, "/", *src);
   1616         pErrorNULL(setS(bpath, -1, 0));
   1617         // generate c
   1618         generateC(*src, bpath);
   1619         //copy(*src, bpath);
   1620         free(bpath);
   1621 
   1622         // convert ch to h
   1623         smallArrayt *deps = getG(cDependencies, rtSmallArrayt, *src);
   1624         if (deps != NULL && lenG(deps) > 0) {
   1625           forEachSmallArray(deps, dep) {
   1626             castS(depName, dep)
   1627             if (isCH(ssGet(depName))) {
   1628               bpath = catS(buildPath, "/", ssGet(depName));
   1629               pErrorNULL(setS(bpath, -2, 'h'));
   1630               pErrorNULL(setS(bpath, -1, 0));
   1631               // generate h
   1632               pError0(copy(ssGet(depName), bpath));
   1633               free(bpath);
   1634             }
   1635             finishG(dep);
   1636     }
   1637       }
   1638         }
   1639           }
   1640 
   1641     // convert ch to h
   1642     forEachSmallArray(inc, INCF) {
   1643       castS(hFilename, INCF);
   1644       if (isCH(ssGet(hFilename))) {
   1645         char *bpath = catS(buildPath, "/", ssGet(hFilename));
   1646         pErrorNULL(setS(bpath, -2, 'h'));
   1647         pErrorNULL(setS(bpath, -1, 0));
   1648         // generate h
   1649         pError0(copy(ssGet(hFilename), bpath));
   1650         free(bpath);
   1651       }
   1652       finishG(INCF);
   1653   }
   1654     }
   1655 
   1656   terminateG(cDependencies);
   1657 
   1658   // compile
   1659 
   1660   // modify dir when main is cp
   1661   // TODO remove -I dir not needed
   1662   if (isCP(mainSrc)) {
   1663     // TODO use another variable, keep dir as the path to the original main src from the command line argument
   1664     // because dir is used to find the packages
   1665     // the new variable should be used in the compile commands below
   1666     pErrorNULL(iPrependS(&dir, "/"));
   1667     pErrorNULL(iPrependS(&dir, buildPath));
   1668   }
   1669 
   1670   // create object path for main file to compile and link program
   1671   char *mainOPath = catS(buildPath, "/", mainSrc);
   1672   if (isC(mainSrc)) {
   1673     pErrorNULL(setS(mainOPath, -1, 'o'));
   1674   }
   1675   else {
   1676     // cp file
   1677     pErrorNULL(setS(mainOPath, -2, 'o'));
   1678     pErrorNULL(setS(mainOPath, -1, 0));
   1679   }
   1680 
   1681 
   1682   // load package.yml
   1683   dum = catS(dir,"/", PACKAGE);
   1684   smallDictt *pkgD = NULL;
   1685   if (fileExists(dum)) {
   1686     createAllocateSmallJson(pkgInfo);
   1687     readFileG(pkgInfo, dum);
   1688     //putsG(pkgInfo);
   1689 
   1690     pkgD = getTopG(pkgInfo, rtSmallDictt);
   1691     finishG(pkgInfo);
   1692   }
   1693   free(dum);
   1694 
   1695   // continue after this when clean is set
   1696   if (!lenG(toCompile) && !sheepyParams.clean) {
   1697     // main doesnt need to be recompiled
   1698     //print 'main doesnt need to be recompiled'
   1699     if (sheepyParams.compileLib) {
   1700       // check if the library exists
   1701       char *libname = appendS(rootMain, ".so");
   1702       if (!fileExists(libname)) {
   1703         free(libname);
   1704         goto linkOrClean;
   1705       }
   1706       free(libname);
   1707       libname = appendS(rootMain, ".a");
   1708       if (!fileExists(libname)) {
   1709         free(libname);
   1710         goto linkOrClean;
   1711       }
   1712       // already compiled > finish
   1713       free(libname);
   1714     }
   1715     else if (!sheepyParams.dontCompileExe) {
   1716       if (!fileExists(buildExePath)) {
   1717         goto linkOrClean;
   1718         //TODO puts(BLD RED "Executable already compiled but not found in build folder!" RST);
   1719         //TODO XFAILURE
   1720       }
   1721       if (!isEmptyS(run)) {
   1722         // the exe is compiled and the user wants to run it
   1723         dum            = malloc(strlen(buildExePath) + strlen(params) + 1 + 1);
   1724         sprintf(dum, "%s %s", buildExePath, params);
   1725         setMaxLogLevel(LOG_DISABLE);
   1726         programResult |= commandNFree(dum);
   1727         setMaxLogLevel(LOG_INFO);
   1728     }
   1729       }
   1730     goto finish;
   1731     // NOTE: free variables allocated after this line before finish:, to avoid freeing initialized buffers
   1732   }
   1733 
   1734   // delete existing executable in buildPath
   1735   // to avoid running an old executable when the compilation fails
   1736   if (fileExists(buildExePath)) {
   1737     pError0(rmAll(buildExePath));
   1738   }
   1739 
   1740   // setup cflags key depending on the options: -c cflags, -t testCflags, -m memcheckCflags
   1741   const char *cflagsKey;
   1742   const char *cflagsAlwaysKey;
   1743 
   1744   if (sheepyParams.test) {
   1745     cflagsKey       = "testCflags";
   1746     cflagsAlwaysKey = "testCflagsAlways";
   1747   }
   1748   else if (sheepyParams.memcheck) {
   1749     cflagsKey       = "memcheckCflags";
   1750     cflagsAlwaysKey = "memcheckCflagsAlways";
   1751   }
   1752   else if (sheepyParams.asan) {
   1753     cflagsKey       = "asanCflags";
   1754     cflagsAlwaysKey = "asanCflagsAlways";
   1755   }
   1756   else {
   1757     cflagsKey       = "cflags";
   1758     cflagsAlwaysKey = "cflagsAlways";
   1759   }
   1760 
   1761 
   1762   // get compile options from user configuration
   1763   char *cflags = NULL;
   1764   if (pkgD) {
   1765     cflags = getG(pkgD, rtChar, cflagsKey);
   1766     if (!cflags) {
   1767       cflags = getG(cfgD, rtChar, cflagsKey);
   1768   }
   1769     }
   1770   else {
   1771     cflags = getG(cfgD, rtChar, cflagsKey);
   1772   }
   1773 
   1774   if (!cflags) {
   1775     cflags = emptySF();
   1776   }
   1777   else {
   1778     cflags = dupG(cflags);
   1779   }
   1780 
   1781   if (hasG(cfgD, cflagsAlwaysKey) && !isBlankG(getG(cfgD, rtChar, cflagsAlwaysKey))) {
   1782     if (!isBlankG(cflags)) {
   1783       pErrorNULL(prependG(&cflags, ' '));
   1784     }
   1785     pErrorNULL(prependG(&cflags, getG(cfgD, rtChar, cflagsAlwaysKey)));
   1786   }
   1787   //logVarG(cflags);
   1788 
   1789   if (sheepyParams.clean) {
   1790     // continue after this when clean is set
   1791     goto linkOrClean;
   1792   }
   1793 
   1794   if (hasG(toCompile, mainSrc)) {
   1795     // compile main c file
   1796 
   1797     FILE* fp;
   1798     char *cc;
   1799     if (!getG(cfgD, rtBool, "tcc")) {
   1800       asprintf(&cc, GCC " %s %s -I %s -I %s -o %s -c %s", cflags, m32bit, dir, libsheepyIncDir, mainOPath, mainCPath);
   1801     }
   1802     else {
   1803       cc = malloc(strlen(dir) + strlen(libsheepyIncDir) + strlen(mainOPath) + strlen(mainCPath) + 1 + 19);
   1804       sprintf(cc, "tcc -I %s -I %s -o %s -c %s", dir, libsheepyIncDir, mainOPath, mainCPath);
   1805     }
   1806     //char *cc = "cat"
   1807     if (getG(cfgD, rtBool, "print_compile_commands")) {
   1808       printf("%s\n", cc);
   1809     }
   1810     programResult |= command(cc);
   1811 
   1812     // compile includes
   1813   }
   1814   char *subcc;
   1815   if (!getG(cfgD, rtBool, "tcc")) {
   1816     asprintf(&subcc, GCC " %s %s -I %s -I %s -c -o", cflags, m32bit, dir, libsheepyIncDir);
   1817   }
   1818   else {
   1819     subcc = malloc(strlen(dir) + strlen(libsheepyIncDir) + 1 + 17);
   1820     sprintf(subcc, "tcc -I %s -I %s -c -o", dir, libsheepyIncDir);
   1821   }
   1822 
   1823   free(cflags);
   1824 
   1825   if (listLengthS(sourcesToCompile) > 0) {
   1826     forEachCharP(sourcesToCompile, src) {
   1827       if (eqG(*src, mainSrc)) {
   1828         // main c file is compiled seperately to take care of the hashbang
   1829         continue;
   1830       }
   1831 
   1832       // set c file path and create object path
   1833       char *cpath;
   1834       char *oPath;
   1835       if (isC(*src)) {
   1836         cpath = catS(actualDir, "/", *src);
   1837         oPath = catS(buildPath, "/", *src);
   1838       }
   1839       else {
   1840         // cp file has been converted to c in buildPath
   1841         cpath = catS(buildPath, "/", *src);
   1842         pErrorNULL(setS(cpath, -1, 0));
   1843         oPath = strdup(cpath);
   1844       }
   1845       pErrorNULL(setS(oPath, -1, 'o'));
   1846 
   1847       // create compile command and run with system
   1848       createAllocateSmallArray(ccmdL);
   1849 
   1850       pErrorNULL(pushG(ccmdL, subcc));
   1851 
   1852       char *buildMirrorD;
   1853       // macOS: touch object to avoid error in macOS
   1854       buildMirrorD = catS("touch ", oPath);
   1855       char *D = shDirname(oPath);
   1856       pError0(mkdirParents(D));
   1857       free(D);
   1858       programResult |= commandNFree(buildMirrorD);
   1859       buildMirrorD = shDirname(oPath);
   1860       if (!fileExists(buildMirrorD)) {
   1861         // create c file dir in homedir/build
   1862         pError0(mkdirParents(buildMirrorD));
   1863       }
   1864       free(buildMirrorD);
   1865       pushNFreeG(ccmdL, oPath);
   1866 
   1867       pErrorNULL(pushG(ccmdL, cpath));
   1868       char *ccmd = joinSG(ccmdL, " ");
   1869       if (getG(cfgD, rtBool, "print_compile_commands")) {
   1870        printf("%s\n", ccmd);
   1871       }
   1872 
   1873       if (sheepyParams.parallel) {
   1874         // compile in parallel
   1875         // create argument list for child process that compile the source code
   1876         char **jobArgs = split(ccmd, " ");
   1877         terminateG(ccmdL);
   1878 
   1879         // remove possible empty strings due to split before
   1880         pErrorNULL(iListCompactS(&jobArgs));
   1881         spawnProc(childProc, jobArgs);
   1882         listFreeS(jobArgs);
   1883       }
   1884       else {
   1885         programResult |= command(ccmd);
   1886       }
   1887       free(ccmd);
   1888   }
   1889     }
   1890 
   1891   free(subcc);
   1892 
   1893   if (sheepyParams.parallel) {
   1894     // wait for the compilation to finish and start linking
   1895     waitForJobs();
   1896   }
   1897 
   1898   // link and run
   1899   linkOrClean:
   1900 
   1901   listFreeS(sourcesToCompile);
   1902 
   1903     // list include objects and libraries in incObjFiles
   1904   char *incObjFiles;
   1905     // collect lflags from packages
   1906   char *packageLflags = NULL;
   1907   createAllocateSmallArray(incList);
   1908   cleanAllocateSmallArray(aLibPackages);
   1909   forEachSmallArray(inc, incL) {
   1910     castS(L, incL)
   1911     if (eqG(L, mainH)) {
   1912       // main c file is already in the compile list, dont add the main object file to avoid multiple definitions
   1913       // while linking
   1914       finishG(L);
   1915       continue;
   1916     }
   1917     char *s = strdup(ssGet(L));
   1918 
   1919     if (hasG(L, PACKAGE_DIR)) {
   1920       // the packages are already compiled
   1921       // use the static library
   1922       if (isH(ssGet(L))) {
   1923         pErrorNULL(setS(s, -1, 'a'));
   1924       }
   1925       else {
   1926         // L is .ch extension
   1927         pErrorNULL(setS(s, -2, 'a'));
   1928         pErrorNULL(setS(s, -1, 0));
   1929       }
   1930       // the path in L can contain subdirectories in the package
   1931       // shpPackages/ini/src/ini.a
   1932       // the package is compiled at the package root
   1933       // remove the the subdirectories in L
   1934       smallStringt *sp = allocG(s);
   1935       smallArrayt *arr = splitG(sp, "/");
   1936       terminateG(sp);
   1937 
   1938       int keepIndex;
   1939       // find package dir in L
   1940       enumerateSmallArray(arr, E, i) {
   1941         if (eqG(E, PACKAGE_DIR)) {
   1942           keepIndex = i;
   1943           finishG(E);
   1944           break;
   1945         }
   1946         finishG(E);
   1947       }
   1948 
   1949       // keep package name and delete subdirectories
   1950       pErrorNULL(delG(arr, keepIndex+2, -1));
   1951       //logVarG(arr);
   1952 
   1953       free(s);
   1954       // add path to main file to be able to run program from any
   1955       // directory
   1956       pErrorNULL(prependG(arr, dir));
   1957       s = joinSG(arr, "/");
   1958       terminateG(arr);
   1959       if (!fileExists(s)) {
   1960         // link library only when it exists, there is no library in header only packages
   1961         free(s);
   1962         finishG(L);
   1963         continue;
   1964       }
   1965 
   1966       // collect lflags for this package
   1967       char *pkg = shDirnameG(s);
   1968       pErrorNULL(iAppendManyS(&pkg, "/", PACKAGE));
   1969       //logVarG(pkg);
   1970       createAllocateSmallJson(pkgJ);
   1971       if (readFileG(pkgJ, pkg)) {
   1972         if (!isBlankG(getG(pkgJ, rtChar, "lflags"))) {
   1973           if (!packageLflags) {
   1974             packageLflags = getNDupG(pkgJ, rtChar, "lflags");
   1975           }
   1976           else {
   1977             pErrorNULL(iAppendManyS(&packageLflags, " ", getG(pkgJ, rtChar, "lflags")));
   1978       }
   1979         }
   1980           }
   1981       free(pkg);
   1982       terminateG(pkgJ);
   1983     }
   1984     else {
   1985       // it is allowed to include an h without a corresponding c file
   1986       if (isH(ssGet(L))) {
   1987         pErrorNULL(setG(L, -1, 'c'));
   1988       }
   1989       else {
   1990         // L is .ch extension
   1991         pErrorNULL(setS(s, -1, 'p'));
   1992       }
   1993       dum = catS(actualDir, "/", ssGet(L));
   1994       if (!fileExistsG(dum)) {
   1995         // no corresponding c file
   1996         // skip
   1997         freeManyS(L,s, dum);
   1998         continue;
   1999       }
   2000       free(dum);
   2001 
   2002       if (isC(ssGet(L))) {
   2003         pErrorNULL(setS(s, -1, 'o'));
   2004       }
   2005       else {
   2006         // L is .ch extension
   2007         pErrorNULL(setS(s, -2, 'o'));
   2008         pErrorNULL(setS(s, -1, 0));
   2009       }
   2010       pErrorNULL(iPrependS(&s, "/"));
   2011       pErrorNULL(iPrependS(&s, buildPath));
   2012     }
   2013     if (getG(s, unusedV, -1) == 'a') {
   2014       pushNFreeG(aLibPackages, s);
   2015     }
   2016     else {
   2017       pushNFreeG(incList, s);
   2018     }
   2019     finishG(L);
   2020   }
   2021 
   2022   // uniquify the object list to avoid multiple symbol definitions
   2023   uniqG(incList, unusedV);
   2024   uniqG(aLibPackages, unusedV);
   2025 
   2026   //logVarG(inc);
   2027   //logVarG(incList)
   2028 
   2029   if (sheepyParams.clean) {
   2030     // clean objects
   2031     // mainCPath is the generacted main in .sheepy
   2032     if (fileExistsG(mainCPath)) {
   2033       if (getG(cfgD, rtBool, "print_compile_commands")) {
   2034         printf("rm %s", mainCPath);
   2035         printf("\n");
   2036       }
   2037       pError0(rmAll(mainCPath));
   2038     }
   2039     if (fileExistsG(mainOPath)) {
   2040       if (getG(cfgD, rtBool, "print_compile_commands")) {
   2041         printf("rm %s", mainOPath);
   2042         printf("\n");
   2043       }
   2044       pError0(rmAll(mainOPath));
   2045     }
   2046     if (getG(cfgD, rtBool, "clean_exe")) {
   2047       // delete executable or library (.a and .so)
   2048       // executable in buildExePath is already deleted (above after if !lenG(toCompile))
   2049       if (fileExists(exePath)) {
   2050         if (getG(cfgD, rtBool, "print_compile_commands")) {
   2051           printf("rm %s", exePath);
   2052           printf("\n");
   2053         }
   2054         pError0(rmAll(exePath));
   2055       }
   2056       char *libname = appendS(rootMain, ".so");
   2057       if (fileExists(libname)) {
   2058         if (getG(cfgD, rtBool, "print_compile_commands")) {
   2059           printf("rm %s", libname);
   2060           printf("\n");
   2061         }
   2062         pError0(rmAll(libname));
   2063       }
   2064       free(libname);
   2065       libname = appendS(rootMain, ".a");
   2066       if (fileExists(libname)) {
   2067         if (getG(cfgD, rtBool, "print_compile_commands")) {
   2068           printf("rm %s", libname);
   2069           printf("\n");
   2070         }
   2071         pError0(rmAll(libname));
   2072       }
   2073       free(libname);
   2074     }
   2075     forEachSmallArray(incList, incL) {
   2076       castS(L, incL)
   2077 
   2078       char *l = ssGet(L);
   2079       if (startsWithG(l, buildPath)) {
   2080         // skip buildPath when looking for PACKAGE_DIR in L
   2081         // because the user can be cleaning a package
   2082         // without this the object files are not deleted
   2083         l += strlen(buildPath);
   2084       }
   2085 
   2086       if (fileExistsG(ssGet(L))) {
   2087         if (getG(cfgD, rtBool, "print_compile_commands")) {
   2088           printf("rm %s", ssGet(L));
   2089           printf("\n");
   2090         }
   2091         pError0(rmAll(ssGet(L)));
   2092       }
   2093       finishG(L);
   2094     }
   2095 
   2096     free(mainOPath);
   2097     terminateG(incList);
   2098     goto finish;
   2099   }
   2100 
   2101   incObjFiles = joinSG(incList, " ");
   2102   if (!incObjFiles) {
   2103     // when there are no other object files beside the main one, set incList to empty string
   2104     emptyS(incObjFiles);
   2105   }
   2106   char *aLibFiles = joinSG(aLibPackages, " ");
   2107   if (!aLibFiles) {
   2108     // when there are no a libs, set aLibFiles to empty string
   2109     emptyS(aLibFiles);
   2110   }
   2111 
   2112   terminateG(incList);
   2113   terminateG(aLibPackages);
   2114   //logVarG(incObjFiles)
   2115 
   2116   // setup lflags key depending on the options: -c lflags, -t testLflags, -m memcheckLflags
   2117   const char *lflagsKey;
   2118 
   2119   if (sheepyParams.test) {
   2120     lflagsKey       = "testLflags";
   2121   }
   2122   else if (sheepyParams.memcheck) {
   2123     lflagsKey       = "memcheckLflags";
   2124   }
   2125   else if (sheepyParams.asan) {
   2126     lflagsKey       = "asanLflags";
   2127   }
   2128   else {
   2129     lflagsKey       = "lflags";
   2130 
   2131     // create link command gcc/tcc exe/lib/no linking
   2132   }
   2133   char *lflags;
   2134   if (pkgD) {
   2135     lflags = getG(pkgD, rtChar, lflagsKey);
   2136     if (!lflags) {
   2137       lflags = getG(cfgD, rtChar, lflagsKey);
   2138   }
   2139     }
   2140   else {
   2141     lflags = getG(cfgD, rtChar, lflagsKey);
   2142   }
   2143   if (!lflags) {
   2144     lflags = "";
   2145   }
   2146 
   2147   lflags = dupG(lflags);
   2148 
   2149   if (!isBlankG(packageLflags)) {
   2150     pErrorNULL(iAppendManyS(&lflags, " ",packageLflags));
   2151     free(packageLflags);
   2152   }
   2153   //logVarG(lflags);
   2154 
   2155   // setup linkWithGold key depending on the options: -c compile, -t test, -m memcheck
   2156   const char *linkWithGoldKey;
   2157 
   2158   if (sheepyParams.test) {
   2159     linkWithGoldKey       = "testLinkWithGold";
   2160   }
   2161   else if (sheepyParams.memcheck) {
   2162     linkWithGoldKey       = "memcheckLinkWithGold";
   2163   }
   2164   else if (sheepyParams.asan) {
   2165     linkWithGoldKey       = "asanLinkWithGold";
   2166   }
   2167   else {
   2168     linkWithGoldKey       = "linkWithGold";
   2169   }
   2170 
   2171   char *ld;
   2172   if (!getG(cfgD, rtBool, "tcc")) {
   2173     char *linker;
   2174     if (getG(cfgD, rtBool, linkWithGoldKey)) {
   2175       linker = " -fuse-ld=gold -Xlinker --threads";
   2176     }
   2177     else {
   2178       linker = "";
   2179     }
   2180 
   2181     if (!sheepyParams.compileLib) {
   2182       if (!sheepyParams.dontCompileExe) {
   2183         asprintf(&ld, GCC " %s -o %s %s %s %s %s/%s "PTHREAD" %s%s %s", m32bit, buildExePath, mainOPath, incObjFiles, aLibFiles, libsheepyDir, libsheepy, lflags, linker, run);
   2184       }
   2185       else {
   2186         ld = emptySF();
   2187     }
   2188       }
   2189     else {
   2190       // link library in current folder
   2191       asprintf(&ld, "gcc %s -shared -o %s.so %s %s %s %s/%s "PTHREAD" %s%s", m32bit, rootMain, mainOPath, incObjFiles, aLibFiles, libsheepyDir, libsheepy, lflags, linker);
   2192   }
   2193     }
   2194   else {
   2195     // TODO update command
   2196     ld = malloc(strlen(buildExePath) + strlen(mainOPath) + strlen(incObjFiles) + strlen(aLibFiles) + strlen(libsheepyDir) + strlen(libsheepy) + strlen(lflags) + strlen(run) + 1 + 23);
   2197     sprintf(ld, "tcc -o %s %s %s %s %s/%s -pthread %s %s", buildExePath, mainOPath, incObjFiles, aLibFiles, libsheepyDir, libsheepy, lflags, run);
   2198   }
   2199 
   2200   free(lflags);
   2201 
   2202   if (getG(cfgD, rtBool, "print_compile_commands")) {
   2203     printf("%s\n", ld);
   2204   }
   2205 
   2206   // hide eventual error from the compiled program
   2207   setMaxLogLevel(LOG_DISABLE);
   2208   programResult |= commandNFree(ld);
   2209   setMaxLogLevel(LOG_INFO);
   2210 
   2211   // build static lib
   2212   if (sheepyParams.compileLib) {
   2213     // link library in current folder
   2214     // options: c create, q append objects, T thin archive for recursive library dependencies in packages
   2215     ld = malloc(strlen(ar32bit) + strlen(rootMain) + strlen(mainOPath) + strlen(incObjFiles) + strlen(aLibFiles) + 1 + 26);
   2216     sprintf(ld, "ar %s -cqT %s.a %s %s %s > /dev/null", ar32bit, rootMain, mainOPath, incObjFiles, aLibFiles);
   2217     if (getG(cfgD, rtBool, "print_compile_commands")) {
   2218       printf("%s\n", ld);
   2219     }
   2220     programResult |= commandNFree(ld);
   2221   }
   2222 
   2223   // mainOPath is not used after link step
   2224   free(mainOPath);
   2225   free(incObjFiles);
   2226   free(aLibFiles);
   2227 
   2228   //puts("LIST SOURCES");
   2229   //TODO use logE in pError0 and setModificationTime and disable logging with setMaxLogLevel
   2230   if (lenG(toCompile) > 0) {
   2231     // copy c file modified dates to newly created object files
   2232     iter(toCompile, info) {
   2233       cast(smallContainert *, infoC, info)
   2234       srcFilet *srcF = getG(infoC, rtVoid, 0);
   2235       //logVarG(srcF->fpath);
   2236 
   2237       if (isH(srcF->bpath) || isCH(srcF->bpath)) {
   2238         char *dep = catS(srcF->bpath, ".dep");
   2239         if (!fileExistsG(dep)) {
   2240           logW("'%s' not found.", dep);
   2241         }
   2242         else {
   2243           pError0(setModificationTime(dep, srcF->st.st_mtime)) {
   2244           // bug in cg_c
   2245       }
   2246         }
   2247           }
   2248       else {
   2249         if (!fileExistsG(srcF->bpath)) {
   2250           logW("'%s' not found.", srcF->bpath);
   2251         }
   2252         else {
   2253           //logP("SET TIME FOR %s", srcF->bpath);
   2254           pError0(setModificationTime(srcF->bpath, srcF->st.st_mtime)) {
   2255           // bug in cg_c
   2256       }
   2257         }
   2258           }
   2259     }
   2260   }
   2261 
   2262   // TODO free toCompile and containers
   2263 
   2264 
   2265   finish:
   2266   // copy exe
   2267   if (isEmptyS(run) && !sheepyParams.dontCompileExe && !sheepyParams.clean) {
   2268     // copy it to main source folder
   2269     pError0(copy(buildExePath, exePath));
   2270     if (programResult) {
   2271       dum = BLD UDL BGYLW RED"Failed to build Executable:"RST" %s\n";
   2272     }
   2273     else {
   2274       dum = BLD GRN"Built Executable:"RST" %s\n";
   2275     }
   2276     printf(dum, exePath);
   2277   }
   2278   if (getG(cfgD, rtBool, "print_compile_commands") && !isEmptyS(run) && !sheepyParams.clean) {
   2279     if (programResult) {
   2280       dum = BLD UDL BGYLW RED"Failed to build Executable:"RST" %s\n";
   2281     }
   2282     else {
   2283       dum = BLD GRN"Built Executable:"RST" %s\n";
   2284     }
   2285     printf(dum, buildExePath);
   2286   }
   2287 
   2288   // free
   2289   free(mainCPath);
   2290   free(buildExePath);
   2291   free(buildPath);
   2292   free(mainH);
   2293   free(mainFilename);
   2294   free(exePath);
   2295   free(mainSrc);
   2296   terminateG(inc);
   2297   free(actualDir);
   2298   free(dir);
   2299   listFreeS(rootMain_l);
   2300   terminateG(paramL);
   2301   free(params);
   2302   free(run);
   2303   free(cfgD);
   2304   terminateG(cfgJ);
   2305   free(homedir);
   2306   finalizeLibsheepy();
   2307 
   2308   exit(programResult);
   2309 }
   2310 
   2311 char *getInclude(smallStringt *L) {
   2312   char *r = NULL;
   2313 
   2314   if (strncmp(ssGet(L), "#include ", strlen("#include ")) == 0 && !hasG(L,"libsheepy.h") && !hasG(L, "libsheepyObject.h")) {
   2315     char **genericLib = extractS(ssGet(L), "<", ">");
   2316     if (listLengthS(genericLib) == 0) {
   2317       char **incFileL = splitSG(L, "\"");
   2318       if (listLengthS(incFileL) > 2) {
   2319         r = strdup(incFileL[1]);
   2320       }
   2321       listFreeS(incFileL);
   2322     }
   2323     listFreeS(genericLib);
   2324   }
   2325   return(r);
   2326 }
   2327 
   2328 void addFileToCompile(char *actualDir, const char *cFileName, char *oPath, smallDictt *toCompile) {
   2329 
   2330   // add c file to compile list
   2331   if (!hasG(toCompile, cFileName)) {
   2332     //logP("ADD TO TOCOMPILE");
   2333     //logVarG(actualDir);
   2334     //logVarG(cFileName);
   2335     //logVarG(oPath);
   2336     srcFilet *srcI;
   2337     // allocate srcFile info, set later
   2338     srcI                      = malloc(sizeof(srcFilet));
   2339     char *tmp                 = catS(actualDir, "/", cFileName);
   2340     pError(stat(tmp, &(srcI->st)))
   2341     srcI->fpath               = strdup(tmp);
   2342     srcI->bpath               = strdup(oPath);
   2343     free(tmp);
   2344     smallContainert *srcFInfo = allocSmallContainer(srcI);
   2345     setNFreeG(toCompile, cFileName, srcFInfo);
   2346 }
   2347   }
   2348 
   2349 void storeHeaderDep(smallArrayt *copyHeaderDepToBuildPath, char *bPath, struct stat hst) {
   2350     srcFilet *srcI = NULL;
   2351 
   2352     // allocate srcFile info, set later
   2353     srcI                      = malloc(sizeof(srcFilet));
   2354     srcI->bpath               = bPath;
   2355     srcI->st                  = hst;
   2356     smallContainert *srcFInfo = allocSmallContainer(srcI);
   2357     pushNFreeG(copyHeaderDepToBuildPath, srcFInfo);
   2358 }
   2359 
   2360 void copyHeaderToBuildPath(char *bPath, struct stat hst) {
   2361 
   2362   // 'copy' header to build path, to check modification time next time
   2363   // the h.dep hold the modification time of the original header
   2364   char *tmp = catS("touch ", bPath, ".dep");
   2365   char *D = shDirname(bPath);
   2366   pError0(mkdirParents(D));
   2367   free(D);
   2368   programResult |= commandNFree(tmp);
   2369 
   2370   // copy modification time
   2371   char *b = catS(bPath, ".dep");
   2372   pError0(setModificationTime(b, hst.st_mtime)) {
   2373   free(b);
   2374   // bug in cg_c - IF in MODIFICATION
   2375 }
   2376   }
   2377 
   2378 
   2379 void copyHeaderDates(char *actualDir, smallArrayt *hDeps, char *buildPath) {
   2380 
   2381   forEachSmallArray(hDeps, dep) {
   2382     char *depName = toStringG(dep);
   2383 
   2384     // get modification time for header depName
   2385     struct stat hst;
   2386     char *tmp = catS(actualDir, "/", depName);
   2387     pError(stat(tmp, &hst))
   2388     free(tmp);
   2389 
   2390     char *bPath = catS(buildPath, "/", depName, ".dep");
   2391     //puts(depName);
   2392     //puts(bPath);
   2393 
   2394     // 'copy' header to build path, to check modification time next time
   2395     tmp = catS("touch ", bPath);
   2396     char *D = shDirname(bPath);
   2397     pError0(mkdirParents(D));
   2398     free(D);
   2399     programResult |= commandNFree(tmp);
   2400 
   2401     // copy modification time
   2402     pError0(setModificationTime(bPath, hst.st_mtime)) {
   2403 
   2404     finishG(dep);
   2405 }
   2406   }
   2407     }
   2408 
   2409 bool symlinkFile(char *path, char *buildPath) {
   2410   bool r = false;;
   2411 
   2412   if (!isCH(path) && !isCP(path) && fileExists(path)) {
   2413     if (!fileExists(buildPath)) {
   2414       // symlink (h) file
   2415       char *D = shDirname(buildPath);
   2416       pError0(mkdirParents(D));
   2417       free(D);
   2418       char *cmd = catS("ln -s ", path, " ", buildPath);
   2419       programResult |= commandNFree(cmd);
   2420     }
   2421     r = true;
   2422   }
   2423   return(r);
   2424 }
   2425 
   2426 bool isC(const char *path) {
   2427 
   2428   if (getS(path, -1) == 'c') {
   2429     return(true);
   2430   }
   2431   else {
   2432     return(false);
   2433 }
   2434   }
   2435 
   2436 bool isH(const char *path) {
   2437 
   2438   if (getS(path, -2) == '.' && getS(path, -1) == 'h') {
   2439     return(true);
   2440   }
   2441   else {
   2442     return(false);
   2443 }
   2444   }
   2445 
   2446 bool isCP(const char *path) {
   2447 
   2448   if (getS(path, -2) == 'c' && getS(path, -1) == 'p') {
   2449     return(true);
   2450   }
   2451   else {
   2452     return(false);
   2453 }
   2454   }
   2455 
   2456 bool isCH(const char *path) {
   2457 
   2458   if (getS(path, -2) == 'c' && getS(path, -1) == 'h') {
   2459     return(true);
   2460   }
   2461   else {
   2462     return(false);
   2463 }
   2464   }
   2465 
   2466 void generateC(char *src, char *bpath) {
   2467 
   2468   createAllocateSmallArray(sheepySrc);
   2469   readFileG(sheepySrc, src);
   2470 
   2471   createAllocateSmallArray(cCode);
   2472 
   2473   generateCBuffers(sheepySrc, cCode);
   2474 
   2475   pError0(writeFileG(cCode, bpath));
   2476 
   2477   terminateG(sheepySrc);
   2478   terminateG(cCode);
   2479 }
   2480 
   2481 
   2482 void generateCBuffers(smallArrayt *sheepySrc, smallArrayt *cCode) {
   2483 
   2484   generatedC  = cCode;
   2485   initiateAllocateSmallArray(&sheepyC);
   2486   initiateAllocateSmallArray(&stateStack);
   2487   initiateAllocateSmallArray(&textStack);
   2488 
   2489   smallStringt *srcO               = joinG(sheepySrc, "\n");
   2490   char *src                        = ssGet(srcO);
   2491   size_t len                       = strlen(src) +2;
   2492   *(src-1)                         = 0;
   2493   quex_Token*     token_p          = NULL;
   2494   size_t          number_of_tokens = 0;
   2495   quex_EasyLexer  qlex;
   2496   bool            start            = true;
   2497 
   2498   /* if len <= QUEX_SETTING_BUFFER_MIN_FALLBACK_N+2 */
   2499   /*   char *tmp = realloc(src-1, QUEX_SETTING_BUFFER_MIN_FALLBACK_N+3) */
   2500   /*   len = QUEX_SETTING_BUFFER_MIN_FALLBACK_N+3 */
   2501   /*   src = tmp+1 */
   2502 
   2503   quex_EasyLexer_from_memory(&qlex, (QUEX_TYPE_LEXATOM*)(src-1), len, (QUEX_TYPE_LEXATOM*)(src+len-2));
   2504 
   2505   //#ifdef PRINT_TOKEN
   2506   const size_t    BufferSize = 1024;
   2507   char            buffer[1024];
   2508   //#endif
   2509   while ((start == true) || (token_p->_id != QUEX_TKN_TERMINATION)) {
   2510     start = false;
   2511     quex_EasyLexer_receive(&qlex, &token_p);
   2512     // process token with the state machine
   2513     char *text = (char*)QUEX_NAME_TOKEN(pretty_char_text)(token_p, buffer, BufferSize);
   2514     range(i, TRANSITIONS) {
   2515       if (stateTkn(i) == -1) {
   2516         // no more next state, stop
   2517         STATEDEFAULTF[stateM](stateM, text);
   2518         break;
   2519       }
   2520       if (token_p->_id == stateTkn(i)) {
   2521         callNxt(i);
   2522         break;
   2523       }
   2524     }
   2525 
   2526     //printf("nxt stateM %s, %s \n", STATESSTR[stateM], QUEX_NAME_TOKEN(get_string)(token_p, buffer, BufferSize));
   2527     ++number_of_tokens;
   2528   }
   2529 
   2530   quex_EasyLexer_destruct(&qlex);
   2531 
   2532   terminateG(srcO);
   2533 
   2534   //printf("| [END] number of token = %i\n", (int)number_of_tokens);
   2535 
   2536   // TODO free stuff
   2537 }
   2538 
   2539 void splash(void) {
   2540 
   2541     putsG(
   2542           "           __  _\n"
   2543           "       .-.'  `; `-._  __  _\n"
   2544           "      (_,         .-:'  `; `-._\n"
   2545           "    ,'o\"(        (_,           )\n"
   2546           "   (__,-'      ,'o\"(  sheepy    )>\n"
   2547           "      (       (__,-'            )\n"
   2548           "       `-'._.--._(             )\n"
   2549           "          |||  |||`-'._.--._.-'\n"
   2550           "                     |||  |||\n"
   2551           "Project:                 " BLD WHT "https://spartatek.se/r/sheepy/file/README.md.html" RST "\n"
   2552           "Libsheepy documentation: " BLD WHT "https://spartatek.se/libsheepy/" RST "\n"
   2553           );
   2554 }
   2555 
   2556 // vim: set expandtab ts=2 sw=2: