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(¶ms, "\"")); 834 pErrorNULL(pushG(¶ms, "\"")); 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(¶ms, "\"")); 1184 pErrorNULL(pushG(¶ms, "\"")); 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 (©AllHeaderDatesToDep, &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 (©AllHeaderDatesToDep, &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 (©AllHeaderDatesToDep, &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(©HeaderDepToBuildPath, 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(©HeaderDepToBuildPath, 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(©HeaderDepToBuildPath, 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(©HeaderDepToBuildPath, 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(©AllHeaderDatesToDep, 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(©HeaderDepToBuildPath, 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(©AllHeaderDatesToDep); 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: