spm.c (57272B)
1 #! /usr/bin/env sheepy 2 // MIT License 3 // 4 // Copyright (c) 2026 Remy Noulin 5 // 6 // Permission is hereby granted, free of charge, to any person obtaining a copy 7 // of this software and associated documentation files (the "Software"), to deal 8 // in the Software without restriction, including without limitation the rights 9 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 // copies of the Software, and to permit persons to whom the Software is 11 // furnished to do so, subject to the following conditions: 12 // 13 // The above copyright notice and this permission notice shall be included in all 14 // copies or substantial portions of the Software. 15 // 16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 // SOFTWARE. 23 // 24 // 25 // sheepy package manager 26 // the published packages have the package information and the source 27 // install downloads and compiles the packages as static and dynamic libraries 28 // 29 // dependencies: tar curl pigz sheepy 30 // 31 // commands: 32 // publish: upload pacakge to spmRegistry 33 // install pkg: install latest package in current folder 34 // install -g pkg: install latest package globally (system wide or specified folder in ~/.sheepy/config.yml) 35 // install pkg@1.0.0: install specific version 36 // 37 // To be able to publish, the registry key has to be set in ~/.sheepy/config.yml 38 // 39 // TODO 40 // add command to set registry key 41 42 #include "libsheepyObject.h" 43 #include "spm.h" 44 #include "common.h" 45 #include "sha256/sha256.h" 46 47 #define internal static 48 49 #include <stdio.h> 50 #include <dirent.h> 51 #include <libgen.h> 52 #include <stdbool.h> 53 #include <string.h> 54 #include <sys/wait.h> 55 #include <stdlib.h> 56 #include <unistd.h> 57 58 #ifndef unitTest 59 #endif 60 int MAIN(int ARGC, char** ARGV); 61 void publish(smallDictt *cfgD, bool pigz); 62 void installPackages(bool isGlobal, smallDictt *cfgD, smallArrayt *options, bool pigz); 63 void childInstall(void *Args); 64 void installAPackage(bool isGlobal, smallDictt *cfgD, smallStringt *o, bool pigz); 65 void compilePackage(char *name, bool isGlobal, smallDictt *cfgD, char *currentVersion); 66 smallDictt *loadYML(char *path); 67 bool installDependencies(bool isGlobal, smallDictt *cfgD, smallDictt *d, const char *name); 68 int testPackage(smallDictt *cfgD, smallDictt *packageyml); 69 void memcheckPackage(smallDictt *cfgD, smallDictt *packageyml); 70 void asanPackage(smallDictt *cfgD, smallDictt *packageyml); 71 void listAllPackages(smallDictt *cfgD, smallArrayt *options); 72 void showPackage(smallDictt *cfgD, smallStringt *o); 73 void showPackageInfo(smallStringt *path); 74 void updatePackages(bool isGlobal, smallDictt *cfgD, smallArrayt *options, bool pigz); 75 void uninstallPackages(bool isGlobal, smallDictt *cfgD, smallArrayt *options); 76 void uninstallDependencies(smallDictt *cfgD, smallDictt *d); 77 void spawnProcDeps(cbFt cb, void *arg); 78 void waitForJobsDeps(void); 79 void findPrint(smallDictt *d); 80 char **requestRegistry(smallDictt *cfgD, smallJsont *json); 81 void showPackageListFromRegistry(char **result, smallJsont *json); 82 void whoamiResult(char **result, smallJsont *json); 83 void profileResult(char **result, smallJsont *json); 84 void unpublishResult(char **result, smallJsont *json); 85 void apikeyResult(char **result, smallJsont *json); 86 void addUserResult(char **result, smallJsont *json); 87 88 int argc; char **argv; 89 90 #define LOCAL false 91 #define GLOBAL true 92 #define CURRENT_YML NULL 93 94 jobst jobsDeps; 95 96 char *localPackageDir; 97 98 #ifndef unitTest 99 // Remove main when running the unit tests 100 #define MAIN main 101 #endif 102 int MAIN(int ARGC, char** ARGV) { 103 bool bum; 104 char *dum = NULL; 105 char *homedir = NULL; 106 bool pigz = false;; 107 108 argc = ARGC; argv = ARGV;;// steps 109 // load config.yml 110 // check arguments 111 // set parameters 112 // check if pigz or gzip are installed 113 // publish package to registry 114 // get registry user key 115 // load package information 116 // get package information 117 // upload package 118 // install package 119 // choose install destination 120 // create package folder 121 // package name and version 122 // download package 123 // extract package in package folder 124 // compile package 125 // build with sheepy 126 // if global then link command to bin from package folder 127 // install dependencies 128 129 // load config.yml 130 smallJsont *cfgJ = loadSheepyConfig(&homedir); 131 smallDictt *cfgD = getTopG(cfgJ, rtSmallDictt); 132 //putsG(cfgJ); 133 134 // check arguments 135 // need at least 1 argument 136 if (argc < 2) { 137 puts("Nothing to do. "BLD GRN"spm help"RST" for help\n"); { 138 goto help; 139 } 140 } 141 142 // find current local package dir 143 localPackageDir = findLocalPackageDir(cfgD); 144 145 //logVarG(localPackageDir); 146 147 if (eqS(argv[1], "-h") || eqS(argv[1], "--help") || eqS(argv[1], "help")) { 148 help: 149 putsG(BLD GRN "Sheepy Package Manager Help" RST); 150 putsG(" " UDL YLW "Environment" RST); 151 putsG(" The configuration is located in " UDL HOME_DIR CONFIG_NAME RST "\n"); 152 char *dum = expandHome(HOME_DIR CONFIG_NAME);; 153 if (!fileExists(dum)) { 154 printf(" %s missing.", dum); 155 } 156 printf(" The build directory is located in " UDL "%sbuild/" RST "\n", HOME_DIR); 157 printf(" The local package repository is located in " UDL "%s" RST "\n", localPackageDir); 158 if (dum) { 159 var tmp = catS(getG(cfgD, rtChar, "system"), "/", PACKAGE_DIR); 160 pErrorNULL(bUniqSlash(tmp)); 161 printf(" The global package repository is located in " UDL "%s" RST "\n", tmp); 162 free(tmp); 163 tmp = catS(getG(cfgD, rtChar, "system"), "/bin"); 164 pErrorNULL(bUniqSlash(tmp)); 165 printf(" Commands from packages are added in " UDL "%s" RST "\n", tmp); 166 free(tmp); 167 } 168 if (hasG(cfgD, "parallelSpm") && !getG(cfgD, rtI32, "parallelSpm")) { 169 puts(BLD RED " Parallel package processing is disabled." RST); 170 } 171 if (hasG(cfgD, "parallelSheepy") && !getG(cfgD, rtI32, "parallelSheepy")) { 172 puts(BLD RED " Parallel compilation is disabled." RST); 173 } 174 free(dum); 175 put 176 putsG("For sheepy help, run: sheepy -h"); 177 put 178 putsG(BLD CYN" new (PKG1) (PKG2)"RST" - create a new empty package, without parameters print default package.yml"); 179 putsG(BLD CYN" add"RST" - add packages in "PACKAGE_DIR" folder to dependencies list in package.yml"); 180 putsG(BLD CYN" publish"RST" - upload package to spm registry"); 181 putsG(BLD CYN" install (PKG1) (PKG2)"RST" - install package from spm registry, without parameters install packages listed in package.yml"); 182 putsG(BLD CYN" -g install PKG1 (PKG2)"RST" - install package globally in the system"); 183 putsG(BLD CYN" uninstall PKG1 (PKG2)"RST" - uninstall package"); 184 putsG(BLD CYN" -g uninstall PKG1 (PKG2)"RST" - uninstall package globally"); 185 putsG(BLD CYN" test"RST" - compile testBin and run"); 186 putsG(BLD CYN" memcheck"RST" - compile memcheckBin and run with the memcheck command"); 187 putsG(BLD CYN" showmc"RST" - show the memcheck command, either in " HOME_DIR CONFIG_NAME " or in package.yml"); 188 putsG(BLD CYN" doc"RST" - generate the documentation by running the documentation command from package.yml"); 189 putsG(BLD CYN" show (PKG1 PKG2)"RST" - show list of package information in the system"); 190 putsG(BLD CYN" -g show (PKG1 PKG2)"RST" - show list of package information installed globally in the system"); 191 putsG(BLD CYN" update|upgrade (PKG1 PKG2)"RST" - update package"); 192 putsG(BLD CYN" -g update|upgrade (PKG1 PKG2)"RST" - update packages globally"); 193 putsG(BLD CYN" find PKG1 (PKG2)"RST" - find package in the registry"); 194 putsG(BLD CYN" top"RST" - top packages in the registry"); 195 putsG(BLD CYN" hot"RST" - hot packages in the registry"); 196 putsG(BLD CYN" latest"RST" - latest packages in the registry"); 197 putsG(BLD CYN" whoami"RST" - user associated with the configured api key"); 198 putsG(BLD CYN" profile USERNAME"RST" - show user profile"); 199 putsG(BLD CYN" unpublish PKG1 (PKG2)"RST" - unpublish pacakges in the registry"); 200 putsG(BLD CYN" info PKG1 (PKG2)"RST" - show package information"); 201 putsG(BLD CYN" adduser"RST" - create an account in the registry"); 202 putsG(BLD CYN" apikey"RST" - get api key for a user, to use the key edit the key field in " HOME_DIR CONFIG_NAME); { 203 XSUCCESS 204 } 205 } 206 207 // set parameters 208 // global is an option associated with install to install a package in the system 209 210 createAllocateSmallArray(options); 211 212 // bum is set to true when a command is found 213 bum = false; 214 rangeFrom(i, 1, argc) 215 if (eqG(argv[i], "publish")) { 216 params.publish += 1; 217 bum = true; 218 } 219 else if (eqG(argv[i], "new")) { 220 params.new += 1; 221 bum = true; 222 } 223 else if (eqG(argv[i], "add")) { 224 params.add += 1; 225 bum = true; 226 } 227 else if (eqG(argv[i], "install")) { 228 params.install += 1; 229 bum = true; 230 } 231 else if (eqG(argv[i], "uninstall")) { 232 params.uninstall += 1; 233 bum = true; 234 } 235 else if (eqG(argv[i], "test")) { 236 params.test += 1; 237 bum = true; 238 } 239 else if (eqG(argv[i], "memcheck")) { 240 params.memcheck += 1; 241 bum = true; 242 } 243 else if (eqG(argv[i], "showmc")) { 244 params.showmcheck += 1; 245 bum = true; 246 } 247 else if (eqG(argv[i], "asan")) { 248 params.asan += 1; 249 bum = true; 250 } 251 else if (eqG(argv[i], "showasan")) { 252 params.showasan += 1; 253 bum = true; 254 } 255 else if (eqG(argv[i], "doc")) { 256 params.doc += 1; 257 bum = true; 258 } 259 else if (eqG(argv[i], "-g")) { 260 params.global += 1; 261 } 262 else if (eqG(argv[i], "show")) { 263 params.show += 1; 264 bum = true; 265 } 266 else if (eqG(argv[i], "update") || eqG(argv[i], "upgrade")) { 267 params.update += 1; 268 bum = true; 269 } 270 else if (eqG(argv[i], "find")) { 271 params.find += 1; 272 bum = true; 273 } 274 else if (eqG(argv[i], "top")) { 275 params.top += 1; 276 bum = true; 277 } 278 else if (eqG(argv[i], "hot")) { 279 params.hot += 1; 280 bum = true; 281 } 282 else if (eqG(argv[i], "latest")) { 283 params.latest += 1; 284 bum = true; 285 } 286 else if (eqG(argv[i], "whoami")) { 287 params.whoami += 1; 288 bum = true; 289 } 290 else if (eqG(argv[i], "profile")) { 291 params.profile += 1; 292 bum = true; 293 } 294 else if (eqG(argv[i], "unpublish")) { 295 params.unpublish += 1; 296 bum = true; 297 } 298 else if (eqG(argv[i], "info")) { 299 params.info += 1; 300 bum = true; 301 } 302 else if (eqG(argv[i], "adduser")) { 303 params.adduser += 1; 304 bum = true; 305 } 306 else if (eqG(argv[i], "apikey")) { 307 params.apikey += 1; 308 bum = true; 309 } 310 else if (params.new || params.install || params.show || params.update || params.uninstall || params.find || params.profile || params.unpublish || params.info) { 311 pushG(options, argv[i]); 312 } 313 314 if (!bum) { 315 putsG(BLD RED "Command not found." RST); 316 XFAILURE 317 } 318 319 int paramsSum = params.install + params.publish + params.show + params.new + params.update + params.uninstall + params.find + params.top + params.hot + params.latest + params.whoami + params.profile + params.unpublish + params.info + params.adduser + params.apikey + params.test + params.memcheck + params.showmcheck + params.asan + params.showasan + params.doc + params.add; 320 if (paramsSum > 1) { 321 putsG(BLD RED "Conflicting commands." RST); 322 XFAILURE 323 } 324 325 if (params.adduser) { 326 327 newUser: 328 printf("Username: "); 329 char *username = readS(); 330 331 if (isBlankG(username)) { 332 free(username); 333 puts(BLD RED "Incorrect input. Enter new username." RST); 334 goto newUser; 335 } 336 337 newName: 338 printf("Name: "); 339 char *name = readS(); 340 341 if (isBlankG(name)) { 342 free(name); 343 puts(BLD RED "Incorrect input. Enter new name." RST); 344 goto newName; 345 } 346 347 newPassword: 348 printf("Password: "); 349 char *password = readPasswordS();; 350 351 printf("Confirm password: "); 352 char *cpassword = readPasswordS(); 353 354 if (isBlankG(password) || !eqG(password, cpassword)) { 355 freeManyS(password, cpassword); 356 puts(BLD RED "Incorrect input, enter new password." RST); 357 goto newPassword; 358 } 359 if (lenG(password) < 8) { 360 freeManyS(password, cpassword); 361 puts(BLD RED "Too short, minimum 8 random characters, enter new password." RST); 362 goto newPassword; 363 } 364 // clear password from memory 365 pError0(zeroS(cpassword)); 366 free(cpassword); 367 368 newEmail: 369 printf("Public Email: "); 370 char *email = readS();; 371 372 if (lenG(email) < 2 || countG(email, '@') != 1 || startsWithG(email, "@") || endsWithG(email, "@")) { 373 free(email); 374 puts(BLD RED "Incorrect input, enter another email address." RST); 375 goto newEmail; 376 } 377 378 //logVarG(username); 379 //logVarG(name); 380 //logVarG(password); 381 //logVarG(email); 382 383 char *hash = sha256S(password);; 384 // clear password from memory 385 pError0(zeroS(password)); 386 free(password); 387 388 createAllocateSmallJson(json); 389 pErrorNULL(setG(json, "method", "adduser")); 390 createSmallArray(a); 391 pErrorNULL(pushG(&a, username)); 392 pErrorNULL(pushG(&a, hash)); 393 pErrorNULL(pushG(&a, email)); 394 pErrorNULL(pushG(&a, name)); 395 pErrorNULL(setG(json, "params", &a)); 396 397 freeManyS(username, hash, email, name); 398 399 //logVarG(json); 400 char **result = requestRegistry(cfgD, json); 401 // Success: result=["Ls28ZCj_i19JsLXxP5o9OcbSN0pj9KTEBNH,Y,IaiyI",""] 402 // Error: result=["","User USERNAME already exists!"] 403 404 addUserResult(result, json); 405 406 terminateG(json); 407 XSUCCESS 408 } 409 410 if (params.apikey) { 411 412 printf("Username: "); 413 char *username = readS(); 414 415 printf("Password: "); 416 char *password = readPasswordS(); 417 418 //logVarG(username); 419 //logVarG(password); 420 421 char *hash = sha256S(password);; 422 // clear password from memory 423 pError0(zeroS(password)); 424 free(password); 425 426 createAllocateSmallJson(json); 427 pErrorNULL(setG(json, "method", "apikey")); 428 createSmallArray(ar); 429 pErrorNULL(pushG(&ar, username)); 430 pErrorNULL(pushG(&ar, hash)); 431 pErrorNULL(setG(json, "params", &ar)); 432 433 freeManyS(username, hash); 434 435 char **result = requestRegistry(cfgD, json); 436 apikeyResult(result, json); 437 438 terminateG(json); 439 XSUCCESS 440 } 441 442 if (params.whoami) { 443 char *key = getG(cfgD, rtChar, "key"); 444 if (!key || eqG(key, "uninitialized")) { 445 puts(BLD RED "user key missing in " HOME_DIR CONFIG_NAME "!" RST); 446 // TODO free stuff? 447 XFAILURE 448 } 449 450 createAllocateSmallJson(json); 451 pErrorNULL(setG(json, "method", "whoami")); 452 pErrorNULL(setG(json, "key", key)); 453 454 char **result = requestRegistry(cfgD, json); 455 // result=[{"username":"remy","name":"Remy Noulin","publicemail":"eqw@wdqd"}] 456 whoamiResult(result, json); 457 458 terminateG(json); 459 XSUCCESS 460 } 461 462 if (params.profile) { 463 if (!lenG(options)) { 464 puts(BLD RED "Missing parameters!" RST); 465 XFAILURE 466 } 467 468 createAllocateSmallJson(json); 469 pErrorNULL(setG(json, "method", "profile")); 470 pErrorNULL(setNFreeG(json, "params", dupG(options))); 471 472 char **result = requestRegistry(cfgD, json); 473 //result=[{"username":"remy","name":"Remy Noulin","publicemail":"eqw@wdqd","packages":["aCmd","cfp","cpy","here","dmce","forest","inotify","mkparents","mve","normalize","easydoneitCTui","searchReplace","sheepyExamples","md4c","preprocessor","md","files","dog","pp","ini","liveserver"]}] 474 profileResult(result, json); 475 476 terminateG(json); 477 XSUCCESS 478 } 479 480 if (params.unpublish) { 481 if (!lenG(options)) { 482 puts(BLD RED "Missing parameters!" RST); 483 XFAILURE 484 } 485 char *key = getG(cfgD, rtChar, "key"); 486 if (!key || eqG(key, "uninitialized")) { 487 puts(BLD RED "user key missing in " HOME_DIR CONFIG_NAME "!" RST); 488 // TODO free stuff? 489 XFAILURE 490 } 491 492 createAllocateSmallJson(json); 493 pErrorNULL(setG(json, "method", "unpublish")); 494 pErrorNULL(setNFreeG(json, "params", dupG(options))); 495 pErrorNULL(setG(json, "key", key)); 496 497 char **result = requestRegistry(cfgD, json); 498 // result=["TT","simpleTemplates"] 499 unpublishResult(result, json); 500 501 terminateG(json); 502 XSUCCESS 503 } 504 505 if (params.info) { 506 if (!lenG(options)) { 507 puts(BLD RED "Missing parameters!" RST); 508 XFAILURE 509 } 510 511 createAllocateSmallJson(json); 512 pErrorNULL(setG(json, "method", "info")); 513 pErrorNULL(setNFreeG(json, "params", dupG(options))); 514 515 char **result = requestRegistry(cfgD, json); 516 showPackageListFromRegistry(result, json); 517 518 terminateG(json); 519 XSUCCESS 520 } 521 522 if (params.top) { 523 createAllocateSmallJson(json); 524 pErrorNULL(setG(json, "method", "top")); 525 526 char **result = requestRegistry(cfgD, json); 527 showPackageListFromRegistry(result, json); 528 529 terminateG(json); 530 XSUCCESS 531 } 532 533 if (params.hot) { 534 createAllocateSmallJson(json); 535 pErrorNULL(setG(json, "method", "hot")); 536 537 char **result = requestRegistry(cfgD, json); 538 showPackageListFromRegistry(result, json); 539 540 terminateG(json); 541 XSUCCESS 542 } 543 544 if (params.latest) { 545 createAllocateSmallJson(json); 546 pErrorNULL(setG(json, "method", "latest")); 547 548 char **result = requestRegistry(cfgD, json); 549 showPackageListFromRegistry(result, json); 550 551 terminateG(json); 552 XSUCCESS 553 } 554 555 if (params.find) { 556 if (!lenG(options)) { 557 puts(BLD RED "Find needs at least one parameter!" RST); 558 XFAILURE 559 } 560 createAllocateSmallJson(json); 561 pErrorNULL(setG(json, "method", "find")); 562 563 // put options in a string in an array 564 createAllocateSmallArray(a); 565 pErrorNULL(pushNFreeG(a, joinG(options, ' '))); 566 pErrorNULL(setNFreeG(json, "params", a)); 567 //logVarG(json); 568 569 char **result = requestRegistry(cfgD, json); 570 showPackageListFromRegistry(result, json); 571 572 terminateG(json); 573 XSUCCESS 574 } 575 576 if (params.new) { 577 if (!lenG(options)) { 578 // show package.yml template 579 putsG(PKG_TEMPLATE); 580 XSUCCESS 581 } 582 583 iter(options, O) { 584 castS(o, O); 585 pError0(mkdirParentsG(o)); 586 pError0(chDirG(o)); 587 sourceFromTemplate(ssGet(o)); 588 pkgFromTemplate(ssGet(o)); 589 pError0(chDirG("..")); 590 } 591 XSUCCESS 592 } 593 594 if (params.show) { 595 if (argc < 3) { 596 // show dependencies when package.yml is in the current directory 597 if (fileExists(PACKAGE)) { 598 smallDictt *d = loadYML(PACKAGE); 599 smallDictt *deps = getG(d, rtSmallDictt, "dependencies"); 600 if (deps) { 601 char **depsL = keysG(deps); 602 if (fileExists(PACKAGE_DIR)) { 603 // list packages install PACKAGE_DIR dir and not listed in dependencies 604 var packagesInDir = readDirDir(PACKAGE_DIR); 605 forEachS(packagesInDir, dir) { 606 if ((!hasG(depsL, dir))) { 607 printf(BLD RED "%s"RST" installed and not in dependencies\n\n", dir); 608 } 609 } 610 freeG(packagesInDir); 611 } 612 fromArrayNFreeG(options, depsL, 0); 613 } 614 terminateG(d); 615 if (lenG(options)) { 616 params.generatedPackageList = 1; 617 } 618 } 619 if (!params.generatedPackageList && fileExists(PACKAGE_DIR)) { 620 // no dependencies in package.yml, check if there is shpPackages folder 621 char **shpp = readDirDir(PACKAGE_DIR); 622 fromArrayNFreeG(options, shpp, 0); 623 if (lenG(options)) { 624 params.generatedPackageList = 1; 625 } 626 } 627 } 628 629 if ((!params.generatedPackageList && (argc < 3)) || ((argc == 3) && (params.global))) { 630 // no package is specified and package.yml not found, list all global packages 631 params.global = 1; 632 listAllPackages(cfgD, options); 633 } 634 635 sortG(options); 636 // show packages in parameters or all packages 637 iter(options, O) { 638 castS(o, O); 639 showPackage(cfgD, o); 640 } 641 XSUCCESS 642 } 643 644 // check if pigz or gzip are installed 645 createAllocateSmallArray(out); 646 createAllocateSmallArray(er); 647 execO("which pigz", out, er); 648 #if (__sun__) 649 if (not startsWithG(getG(out, rtChar, 0) , "no pigz")) { 650 pigz = true; 651 } 652 else { 653 emptyG(out); 654 execO("which gzip", out, er); 655 if (startsWithG(getG(out, rtChar, 0) , "no gzip")) { 656 puts(BLD RED "Neither pigz nor gunzip are not found." RST); 657 XFAILURE 658 } 659 } 660 #else // #if (__sun__) 661 if (lenG(out)) { 662 pigz = true; 663 } 664 else { 665 execO("which gzip", out, er); 666 if (!lenG(out)) { 667 puts(BLD RED "Neither pigz nor gunzip are not found." RST); 668 XFAILURE 669 } 670 } 671 #endif 672 // #if __sun__ 673 terminateManyO(out, er); 674 675 if (params.update) { 676 if (argc < 3) { 677 // update/install dependencies when package.yml is in the current directory 678 if (fileExists(PACKAGE)) { 679 smallDictt *d = loadYML(PACKAGE); 680 smallDictt *deps = getG(d, rtSmallDictt, "dependencies"); 681 if (deps) { 682 char **depsL = keysG(deps); 683 if (fileExists(PACKAGE_DIR)) { 684 // list packages install PACKAGE_DIR dir and not listed in dependencies 685 var packagesInDir = readDirDir(PACKAGE_DIR); 686 forEachS(packagesInDir, dir) { 687 if ((!hasG(depsL, dir))) { 688 printf(BLD RED "%s"RST" installed and not in dependencies, to update this package run: "BLD"spm update %s"RST"\n\n", dir, dir); 689 } 690 } 691 freeG(packagesInDir); 692 } 693 fromArrayNFreeG(options, depsL, 0); 694 } 695 terminateG(d); 696 if (lenG(options)) { 697 params.generatedPackageList = 1; 698 } 699 } 700 if (!params.generatedPackageList && fileExists(PACKAGE_DIR)) { 701 // no dependencies in package.yml, check if there is shpPackages folder 702 char **shpp = readDirDir(PACKAGE_DIR); 703 fromArrayNFreeG(options, shpp, 0); 704 if (lenG(options)) { 705 params.generatedPackageList = 1; 706 } 707 } 708 } 709 710 if ((!params.generatedPackageList && (argc < 3)) || ((argc == 3) && (params.global))) { 711 // no package is specified and package.yml not found, list all global packages 712 params.global = 1; 713 listAllPackages(cfgD, options); 714 if (lenG(options)) { 715 params.generatedPackageList = 1; 716 } 717 } 718 719 sortG(options); 720 // update packages in parameters or all packages 721 updatePackages(params.global, cfgD, options, pigz); 722 XSUCCESS 723 } 724 725 if (params.add) { 726 if (not isDir(PACKAGE_DIR)) { 727 puts("'"PACKAGE_DIR"' folder not found in current path. Nothing to do."); 728 } 729 else { 730 if (!fileExists(PACKAGE)) { 731 // command line is spm install and there is no package.yml 732 puts(BLD RED "'package.yml' in current path!" RST); 733 XFAILURE 734 } 735 char **installedPackages = readDirDir(PACKAGE_DIR); 736 if (not isEmptyG(installedPackages)) { 737 createSmallJson(pkgInfo); 738 readFileG(&pkgInfo, PACKAGE); 739 smallDictt *pkgDeps = getG(&pkgInfo, rtSmallDictt, "dependencies"); 740 if (not pkgDeps) { 741 // there are no dependencies in package.yml 742 initiateG(&pkgDeps); 743 forEachS(installedPackages, pkg) { 744 // add pkg to dependency list in package.yml 745 pErrorNULL(setG(pkgDeps, pkg, "")); 746 printf("Added %s to dependencies", pkg); 747 printf("\n"); 748 } 749 pErrorNULL(setNFreeG(&pkgInfo, "dependencies", pkgDeps)); 750 } 751 else { 752 forEachS(installedPackages, pkg) { 753 if (not hasG(pkgDeps, pkg)) { 754 // add pkg to dependency list in package.yml 755 pErrorNULL(setG(pkgDeps, pkg, "")); 756 printf("Added %s to dependencies", pkg); 757 printf("\n"); 758 } 759 } 760 pErrorNULL(setNFreePG(&pkgInfo, "dependencies", pkgDeps)); 761 } 762 pError0(writeFileG(&pkgInfo, PACKAGE)); 763 freeG(&pkgInfo); 764 } 765 listFreeS(installedPackages); 766 } 767 XSUCCESS 768 } 769 770 // TODO check that package name is valid: not -g, publish, install 771 // publish package to registry 772 if (params.publish) { 773 publish(cfgD, pigz); 774 } 775 776 if (params.install) { 777 if (argc < 3) { 778 // install dependencies when package.yml is in the current directory 779 if (!fileExists(PACKAGE)) { 780 // command line is spm install and there is no package.yml 781 puts(BLD RED "Package name missing!" RST); 782 XFAILURE 783 } 784 smallDictt *d = loadYML(PACKAGE); 785 // install dependencies locally 786 installDependencies(LOCAL, cfgD, d, CURRENT_YML); 787 terminateG(d); 788 XSUCCESS 789 } 790 791 installPackages(params.global, cfgD, options, pigz); 792 } 793 794 if (params.uninstall) { 795 if (argc < 3) { 796 // install dependencies when package.yml is in the current directory 797 if (!fileExists(PACKAGE)) { 798 // command line is spm install and there is no package.yml 799 puts(BLD RED "Package name missing!" RST); 800 XFAILURE 801 } 802 smallDictt *d = loadYML(PACKAGE); 803 // install dependencies locally 804 uninstallDependencies(cfgD, d); 805 terminateG(d); 806 XSUCCESS 807 } 808 809 uninstallPackages(params.global, cfgD, options); 810 } 811 812 if (params.test) { 813 if (!fileExists(PACKAGE)) { 814 // command line is spm test and there is no package.yml 815 puts(BLD RED "Package name missing!" RST); 816 XFAILURE 817 } 818 smallDictt *d = loadYML(PACKAGE); 819 int exitCode = testPackage(cfgD, d);; 820 terminateG(d); 821 exit(exitCode); 822 } 823 824 if (params.memcheck) { 825 if (!fileExists(PACKAGE)) { 826 // command line is spm memcheck and there is no package.yml 827 puts(BLD RED "Package name missing!" RST); 828 XFAILURE 829 } 830 smallDictt *d = loadYML(PACKAGE); 831 memcheckPackage(cfgD, d); 832 terminateG(d); 833 } 834 835 if (params.showmcheck) { 836 smallDictt *packageyml = NULL; 837 if (fileExists(PACKAGE)) { 838 packageyml = loadYML(PACKAGE); 839 } 840 const char *mcheckCmd = NULL; 841 if (packageyml and hasG(packageyml, "memcheckCmd")) { 842 mcheckCmd = getG(packageyml, rtChar, "memcheckCmd"); 843 } 844 else if (hasG(cfgD, "memcheckCmd")) { 845 mcheckCmd = getG(cfgD, rtChar, "memcheckCmd"); 846 } 847 if (mcheckCmd) { 848 printf("Current memcheck: " BLD GRN "%s" RST "\n", mcheckCmd); 849 } 850 else { 851 puts(BLD RED "No memcheck command (memcheckCmd) found, searched " HOME_DIR CONFIG_NAME " and package.yml" RST); 852 } 853 terminateG(packageyml); 854 } 855 856 if (params.asan) { 857 if (!fileExists(PACKAGE)) { 858 // command line is spm memcheck and there is no package.yml 859 puts(BLD RED "Package name missing!" RST); 860 XFAILURE 861 } 862 smallDictt *d = loadYML(PACKAGE); 863 asanPackage(cfgD, d); 864 terminateG(d); 865 } 866 867 if (params.showasan) { 868 smallDictt *packageyml = NULL; 869 if (fileExists(PACKAGE)) { 870 packageyml = loadYML(PACKAGE); 871 } 872 const char *asanCmd = NULL; 873 if (packageyml and hasG(packageyml, "asanCmd")) { 874 asanCmd = getG(packageyml, rtChar, "asanCmd"); 875 } 876 else if (hasG(cfgD, "asanCmd")) { 877 asanCmd = getG(cfgD, rtChar, "asanCmd"); 878 } 879 if (asanCmd) { 880 printf("Current asan command: " BLD GRN "%s" RST "\n", asanCmd); 881 } 882 else { 883 puts(BLD RED "No asan command (asanCmd) found, searched " HOME_DIR CONFIG_NAME " and package.yml" RST); 884 } 885 terminateG(packageyml); 886 } 887 888 if (params.doc) { 889 smallDictt *d = NULL; 890 if (fileExists(PACKAGE)) { 891 d = loadYML(PACKAGE); 892 } 893 if (d and hasG(d, "documentationCmd")) { 894 if (getG(cfgD, bum, "print_compile_commands")) { 895 printf("%s\n", getG(d, rtChar, "documentationCmd")); 896 } 897 pErrorNot0(system(getG(d, rtChar, "documentationCmd"))); 898 } 899 else { 900 puts(BLD RED "No documentation command (documentationCmd) found in package.yml" RST); 901 } 902 terminateG(d); 903 } 904 905 906 terminateManyG(options, cfgJ); 907 finishG(cfgD); 908 free(homedir); 909 finalizeLibsheepy(); 910 XSUCCESS 911 } 912 913 void publish(smallDictt *cfgD, bool pigz) { 914 char *cmd = NULL; 915 bool bum; 916 917 // get registry user key 918 char *key = getG(cfgD, rtChar, "key"); 919 if (!key) { 920 puts(BLD RED "user key missing in " HOME_DIR CONFIG_NAME "!" RST); 921 // TODO free stuff? 922 XFAILURE 923 } 924 925 // load package information 926 if (!fileExists(PACKAGE)) { 927 puts(BLD RED "'package.yml' missing!" RST); 928 // TODO free stuff? 929 XFAILURE 930 } 931 932 smallDictt *d = loadYML(PACKAGE); 933 // TODO check yml validity 934 935 // get package information 936 char *name = getG(d, rtChar, "name"); 937 if (!name) { 938 puts(BLD RED "Package name missing in package.yml!" RST); 939 // TODO free stuff? 940 XFAILURE 941 } 942 if (eqG(name, "sheepy") || eqG(name, "spm")) { 943 puts(BLD RED "Package name not allowed! (sheepy and spm)" RST); 944 // TODO free stuff? 945 XFAILURE 946 } 947 948 char *version = getG(d, rtChar, "version"); 949 if (!version) { 950 puts(BLD RED "Version missing in package.yml!" RST); 951 // TODO free stuff? 952 XFAILURE 953 } 954 955 // upload package 956 if (pigz) { 957 cmd = replaceManyS(UPLOAD_PKG_ZIP, "$NAME$", name, "$VERSION$", version, "$KEY$", key); 958 } 959 else { 960 cmd = replaceManyS(UPLOAD_PKG_ZIP, "pigz -9", "gzip -9", "$NAME$", name, "$VERSION$", version, "$KEY$", key); 961 } 962 if (getG(cfgD, bum, "print_compile_commands")) { 963 printf("%s\n", cmd); 964 } 965 pErrorNot0(systemNFreeG(cmd)); 966 967 terminateG(d); 968 } 969 970 void installPackages(bool isGlobal, smallDictt *cfgD, smallArrayt *options, bool pigz) { 971 972 // choose install destination 973 char *packageDir; 974 if (!isGlobal) { 975 // install package in current folder 976 packageDir = strdup(localPackageDir); 977 } 978 else { 979 // install package in system 980 packageDir = catS(getG(cfgD, rtChar, "system") , "/", PACKAGE_DIR); 981 } 982 983 pErrorNULL(iExpandHome(&packageDir)); 984 985 // create package folder 986 if (!fileExists(packageDir)) { 987 pError0(mkdirParents(packageDir)); 988 } 989 990 char *cwd = getCwd(); 991 pError0(chDir(packageDir)); 992 993 //logVarG(options); 994 995 // initialize child process list 996 staticArrayInit(jobs); 997 998 char *cwdPackage = appendS(cwd, "/" PACKAGE); 999 if (!lenG(options) && isGlobal && fileExists(cwdPackage)) { 1000 printf("installing current package %s\n", cwd); 1001 char *pkgName = strdup(basename(cwd)); 1002 if (isDir(pkgName)) { 1003 pError0(rmAll(pkgName)); 1004 } 1005 pError0(copy(cwd, packageDir)); 1006 compilePackage(pkgName, isGlobal, cfgD, "version"); 1007 free(pkgName); 1008 } 1009 freeManyS(packageDir, cwdPackage); 1010 1011 iter(options, O) { 1012 castS(o, O); 1013 installArgst args; 1014 args.isGlobal = isGlobal; 1015 args.cfgD = cfgD; 1016 args.o = o; 1017 args.pigz = pigz; 1018 1019 // parallel has to be defined and 0 to disable parallel spm 1020 if (hasG(cfgD, "parallelSpm") && !getG(cfgD, rtI32, "parallelSpm")) { 1021 childInstall(&args); 1022 } 1023 else { 1024 spawnProc(childInstall, &args); 1025 } 1026 } 1027 1028 pError0(chDir(cwd)); 1029 free(cwd); 1030 1031 // wait for the jobs to finish 1032 waitForJobs(); 1033 } 1034 1035 void childInstall(void *Args) { 1036 1037 cast(installArgst *, args, Args); 1038 installAPackage(args->isGlobal, args->cfgD, args->o, args->pigz); 1039 if (!hasG(args->cfgD, "parallelSpm") || getG(args->cfgD, rtI32, "parallelSpm")) { 1040 XSUCCESS 1041 } 1042 } 1043 1044 void installAPackage(bool isGlobal, smallDictt *cfgD, smallStringt *o, bool pigz) { 1045 char *cmd = NULL; 1046 1047 char *cwd = getCwd(); 1048 if (!isWritable(cwd)) { 1049 logC(BLD RED"%s is not writable. Stop."RST, cwd); 1050 free(cwd); 1051 XFAILURE 1052 } 1053 free(cwd); 1054 1055 // package name and version 1056 char *name; 1057 char *version = NULL; 1058 1059 smallArrayt *spl = splitG(o, "@");; 1060 if (lenG(spl) == 1) { 1061 name = ssGet(o); 1062 cmd = replaceS_max(DOWNLOAD_PKG, "$$", name); 1063 } 1064 else { 1065 name = getG(spl, rtChar, 0); 1066 version = getG(spl, rtChar, 1); 1067 cmd = replaceManyS(DOWNLOAD_PKG_VERSION, "$NAME$", name, "$VERSION$", version); 1068 } 1069 1070 // current version is checked against package version in compilePackage to eventually update the dependencies 1071 char *currentVersion = NULL; 1072 char *path = catS(name, "/", PACKAGE); 1073 if (fileExists(path)) { 1074 smallDictt *d = loadYML(path); 1075 currentVersion = getNDupG(d, rtChar, "version"); 1076 terminateG(d); 1077 } 1078 1079 free(path); 1080 1081 // download package 1082 // TODO save as pkg@version 1083 if (getG(cfgD, rtBool, "print_compile_commands")) { 1084 printf("%s\n", cmd); 1085 } 1086 pErrorNot0(systemNFree(cmd)); 1087 1088 // detect download errors 1089 char *s = catS(name, ".tar.gz");; 1090 if (!fileExists(s)) { 1091 printf(BLD RED "Download error: %s not found" RST "\n", name); 1092 free(s); 1093 XFAILURE 1094 } 1095 char errorBuf[17]; 1096 pErrorNULL(bLReadFileToS(s, errorBuf, sizeof(errorBuf))); 1097 if (startsWithG(errorBuf, "No version found") or startsWithG(errorBuf, "Internal Error.") or startsWithG(errorBuf, "package does not")) { 1098 printf(BLD RED "Download error: %s not found" RST "\n", name); 1099 free(s); 1100 XFAILURE 1101 } 1102 free(s); 1103 1104 // extract package in package folder 1105 if (pigz) { 1106 cmd = replaceS_max(EXTRACT_PKG, "$$", name); 1107 } 1108 else { 1109 cmd = replaceManyS(EXTRACT_PKG,"unpigz ", "gunzip ", "$$", name); 1110 } 1111 if (getG(cfgD, rtBool, "print_compile_commands")) { 1112 printf("%s\n", cmd); 1113 } 1114 pErrorNot0(systemNFreeG(cmd)); 1115 1116 // compile package 1117 compilePackage(name, isGlobal, cfgD, currentVersion); 1118 terminateG(spl); 1119 free(currentVersion); 1120 } 1121 1122 void compilePackage(char *name, bool isGlobal, smallDictt *cfgD, char *currentVersion) { 1123 char *cmd = NULL; 1124 char *version = NULL; 1125 1126 cmd = catS(name, "/", PACKAGE); 1127 smallDictt *d = loadYML(cmd); 1128 free(cmd); 1129 1130 version = getG(d, rtChar, "version"); 1131 char *bin = getG(d, rtChar, "bin"); 1132 bool isCompiled = false; 1133 1134 if (eqG(currentVersion, version)) { 1135 if (bin) { 1136 // check if the package is already compiled 1137 char *tbin = dupG(bin); 1138 // remove extension 1139 if (getS(tbin, -1) == 'c') { 1140 // c extension 1141 pError0(setG(tbin, -2, 0)); 1142 } 1143 else if (getS(tbin, -2) == 'c' && getS(tbin, -1) == 'p') { 1144 // cp extension 1145 pError0(setG(tbin, -3, 0)); 1146 } 1147 // is execautable or library: detect h file 1148 char *h = catS(name, "/", tbin, ".h"); 1149 bool isLib = fileExists(h); 1150 free(h); 1151 if (isLib) { 1152 // check .a and .so 1153 char *libname = catS(name, "/", tbin, ".a"); 1154 bool a = fileExists(libname); 1155 free(libname); 1156 libname = catS(name, "/", tbin, ".so"); 1157 bool so = fileExists(libname); 1158 free(libname); 1159 isCompiled = a && so; 1160 } 1161 else { 1162 // check executable 1163 char *exe = catS(name, "/", tbin); 1164 isCompiled = fileExists(exe); 1165 free(exe); 1166 } 1167 free(tbin); 1168 if (isCompiled) { 1169 goto checkDependencies; 1170 } 1171 } 1172 else { 1173 checkDependencies: 1174 printf("%s@%s is already installed. Checking dependencies...", name, version); 1175 printf("\n"); 1176 } 1177 } 1178 1179 // install dependencies 1180 // the goal with hasDependencies is to detect when dependencies are updated/compiled and recompile the package 1181 // even when isCompiled is true 1182 // TODO convert sheepy and spm to library and return the status (installed, updated, failed) to decide if this 1183 // package needs to be recompiled 1184 bool hasDependencies = false; 1185 if (bin) { 1186 // local, even when the package is installed globally 1187 hasDependencies = installDependencies(LOCAL, cfgD, d, name); 1188 } 1189 else { 1190 // metapackage 1191 // install dependencies locally or globally 1192 if (!isGlobal || !params.update || !params.generatedPackageList) { 1193 // dont update dependencies in global metapackage 1194 hasDependencies = installDependencies(isGlobal, cfgD, d, name); 1195 } 1196 } 1197 1198 if (isCompiled && hasDependencies) { 1199 cmd = replaceManyS(DELETE_PKG, "N", name, "B", bin); 1200 if (getG(cfgD, rtBool, "print_compile_commands")) { 1201 printf("%s\n", cmd); 1202 } 1203 if (systemNFreeG(cmd)) { 1204 printf(BLD RED "Error deleting: %s" RST "\n", name); 1205 } 1206 } 1207 1208 1209 if ((bin && !isCompiled) || (bin && isCompiled && hasDependencies)) { 1210 // this package in a bin line, it is not a metapackage 1211 1212 // build with sheepy 1213 // compile lib when there is 'bin'.h in the package 1214 // the executable is stored in the package folder 1215 cmd = catS(name, "/", bin); 1216 pErrorNULL(setS(cmd, -1, 'h')); 1217 if (getG(cfgD, rtBool, "print_compile_commands")) { 1218 printf("%s\n", cmd); 1219 } 1220 1221 char *build; 1222 if (fileExists(cmd)) { 1223 build = BUILD_PKG; 1224 } 1225 else { 1226 build = BUILD_EXEONLY; 1227 } 1228 1229 free(cmd); 1230 cmd = replaceManyS(build, "N", name, "B", bin); 1231 if (getG(cfgD, rtBool, "print_compile_commands")) { 1232 printf("%s\n", cmd); 1233 } 1234 if (systemNFreeG(cmd)) { 1235 // compile error, show compile help text 1236 printf(BLD RED "Error compiling: %s" RST "\n", name); 1237 if (getG(d, rtChar, "compileHelp")) { 1238 printf(BLD WHT "%s" RST "\n", getG(d, rtChar, "compileHelp")); 1239 } 1240 } 1241 1242 if (isGlobal) { 1243 // link command to bin from package folder 1244 char *pkgBin = strdup(basename(bin)); 1245 char **rootBin_l = split(pkgBin, "."); 1246 char *rootBin = rootBin_l[0]; 1247 // path to bin/cmd to check if it already exists 1248 char *bin = catS("../bin/", rootBin); 1249 cmd = replaceManyS(INSTALL_COMMAND, "$PACKAGE_DIR$", PACKAGE_DIR, "$name$", name, "$bin$", rootBin); 1250 free(pkgBin); 1251 listFreeS(rootBin_l); 1252 1253 if (fileExists(bin)) { 1254 if (!isLink(bin)) { 1255 printf(BLD RED "%s is not a link, spm can't install this package completely, run: %s\n" RST, bin, cmd); 1256 } 1257 free(cmd); 1258 goto skipLink; 1259 } 1260 1261 pErrorNULL(iUniqSlash(cmd)); 1262 1263 if (getG(cfgD, rtBool, "print_compile_commands")) { 1264 printf("%s\n", cmd); 1265 } 1266 pErrorNot0(systemNFreeG(cmd)); 1267 skipLink: 1268 free(bin); 1269 } 1270 } 1271 1272 terminateG(d); 1273 } 1274 1275 smallDictt *loadYML(char *path) { 1276 1277 createAllocateSmallJson(pkgInfo); 1278 readFileG(pkgInfo, path); 1279 //logVarG(pkgInfo); 1280 1281 smallDictt *d = getTopG(pkgInfo, rtSmallDictt); 1282 finishG(pkgInfo); 1283 return(d); 1284 } 1285 1286 bool installDependencies(bool isGlobal, smallDictt *cfgD, smallDictt *d, const char *name) { 1287 char **cmd = NULL; 1288 bool r = false;; 1289 1290 smallDictt *deps = getG(d, rtSmallDictt, "dependencies"); 1291 if (deps) { 1292 r = true; 1293 // initialize child process list 1294 staticArrayInit(jobsDeps); 1295 1296 char **depsL = keysG(deps); 1297 1298 char *cwd = getCwd(); 1299 char *global; 1300 if (isGlobal) { 1301 global = "-g"; 1302 } 1303 else { 1304 // install dependencies inside current package 1305 // when name is NULL, the spm is run in current package, there is no need to change dir 1306 if (name) { 1307 pError0(chDir(name)); 1308 } 1309 // create PACKAGE_DIR shpPackages to install locally 1310 pError0(mkdirParents(PACKAGE_DIR)); 1311 global = ""; 1312 } 1313 1314 forEachCharP(depsL, dep) { 1315 cmd = listCreateS(argv[0], "install", global, *dep); 1316 pErrorNULL(iListCompactS(&cmd)); 1317 if (getG(cfgD, rtBool, "print_compile_commands")) { 1318 logNFree(join(cmd, " ")); 1319 } 1320 1321 // parallel has to be defined and 0 to disable parallel spm 1322 if (hasG(cfgD, "parallelSpm") && !getG(cfgD, rtI32, "parallelSpm")) { 1323 pErrorNot0(systemNFreeG(join(cmd, " "))); 1324 } 1325 else { 1326 spawnProcDeps(childProc, cmd); 1327 } 1328 listFreeS(cmd); 1329 } 1330 1331 pError0(chDir(cwd)); 1332 free(cwd); 1333 listFreeS(depsL); 1334 finishG(deps); 1335 1336 // wait for the dependencies to install 1337 waitForJobsDeps(); 1338 } 1339 return(r); 1340 } 1341 1342 int testPackage(smallDictt *cfgD, smallDictt *packageyml) { 1343 int exitCode = 1;; 1344 1345 if (hasG(packageyml, "testBin")) { 1346 char *bin = getNDupG(packageyml, rtChar, "testBin"); 1347 1348 // dont need to delete intermediary files because the test is compiled in a seperate folder 1349 // (delete intermediary files) 1350 1351 // compile test 1352 if (getG(cfgD, rtBool, "print_compile_commands")) { 1353 printf("sheepy -t %s\n", bin); 1354 } 1355 if (commandf("sheepy -t %s", bin)) { 1356 XFAILURE 1357 } 1358 1359 // remove extension from testBin 1360 if ((getS(bin, -2) == '.') and (getS(bin, -1) == 'c')) { 1361 pErrorNULL(setS(bin, -2, 0)); 1362 } 1363 1364 if (getG(cfgD, rtBool, "print_compile_commands")) { 1365 printf("%s\n", bin); 1366 } 1367 exitCode = command(bin);; 1368 1369 if (getG(cfgD, rtBool, "clean_exe")) { 1370 if (getG(cfgD, rtBool, "print_compile_commands")) { 1371 printf("rm %s", bin); 1372 printf("\n"); 1373 } 1374 pError0(rmAll(bin)); 1375 } 1376 } 1377 1378 return(exitCode); 1379 } 1380 1381 void memcheckPackage(smallDictt *cfgD, smallDictt *packageyml) { 1382 1383 if (!hasG(packageyml, "memcheckBin")) { 1384 putsG(BLD RED "memcheckBin not set in " PACKAGE RST); 1385 } 1386 else { 1387 const char *mcheckCmd; 1388 if (hasG(packageyml, "memcheckCmd")) { 1389 mcheckCmd = getG(packageyml, rtChar, "memcheckCmd"); 1390 } 1391 else if (hasG(cfgD, "memcheckCmd")) { 1392 mcheckCmd = getG(cfgD, rtChar, "memcheckCmd"); 1393 } 1394 else { 1395 // memcheck command not found 1396 putsG(BLD RED "memcheck command not found. Searched " PACKAGE " and " HOME_DIR CONFIG_NAME RST); 1397 return; 1398 } 1399 1400 char *bin = getNDupG(packageyml, rtChar, "memcheckBin"); 1401 1402 if (!bin) { 1403 putsG(BLD RED "memcheckBin is not set in " PACKAGE RST); 1404 return; 1405 } 1406 1407 if (!fileExists(bin)) { 1408 printf(BLD RED "memcheckBin %s not found" RST, bin); 1409 return; 1410 } 1411 1412 // dont need to delete intermediary files because the test is compiled in a seperate folder 1413 // (delete intermediary files) 1414 1415 // compile test 1416 if (getG(cfgD, rtBool, "print_compile_commands")) { 1417 printf("sheepy -m %s\n", bin); 1418 } 1419 if (commandf("sheepy -m %s", bin)) { 1420 XFAILURE 1421 } 1422 1423 // remove extension from memcheckBin 1424 if ((getS(bin, -2) == '.') and (getS(bin, -1) == 'c')) { 1425 pErrorNULL(setS(bin, -2, 0)); 1426 } 1427 1428 // TODO due to a weird bug in debian stretch 180324, I run memcheckBin before running it again with valgrind 1429 // TODO it should not be necessary: "%s %s", mcheckCmd, bin 1430 if (getG(cfgD, rtBool, "print_compile_commands")) { 1431 printf("%s ; %s %s\n", bin, mcheckCmd, bin); 1432 } 1433 int r; 1434 spmAssign(r, commandf("%s ; %s %s\n", bin, mcheckCmd, bin)); 1435 1436 1437 if (getG(cfgD, rtBool, "clean_exe")) { 1438 if (getG(cfgD, rtBool, "print_compile_commands")) { 1439 printf("rm %s", bin); 1440 printf("\n"); 1441 } 1442 pError0(rmAll(bin)); 1443 } 1444 1445 exit(r); 1446 } 1447 } 1448 1449 1450 void asanPackage(smallDictt *cfgD, smallDictt *packageyml) { 1451 1452 if (!hasG(packageyml, "asanBin")) { 1453 putsG(BLD RED "asanBin not set in " PACKAGE RST); 1454 } 1455 else { 1456 const char *asanCmd; 1457 if (hasG(packageyml, "asanCmd")) { 1458 asanCmd = getG(packageyml, rtChar, "asanCmd"); 1459 } 1460 else if (hasG(cfgD, "asanCmd")) { 1461 asanCmd = getG(cfgD, rtChar, "asanCmd"); 1462 } 1463 else { 1464 // asan command not found 1465 putsG(BLD RED "asan command not found. Searched " PACKAGE " and " HOME_DIR CONFIG_NAME RST); 1466 return; 1467 } 1468 1469 char *bin = getNDupG(packageyml, rtChar, "asanBin"); 1470 1471 if (!bin) { 1472 putsG(BLD RED "asanBin is not set in " PACKAGE RST); 1473 return; 1474 } 1475 1476 if (!fileExists(bin)) { 1477 printf(BLD RED "asanBin %s not found" RST, bin); 1478 return; 1479 } 1480 1481 // dont need to delete intermediary files because the test is compiled in a seperate folder 1482 // (delete intermediary files) 1483 1484 // compile test 1485 if (getG(cfgD, rtBool, "print_compile_commands")) { 1486 printf("sheepy -a %s\n", bin); 1487 } 1488 if (commandf("sheepy -a %s", bin)) { 1489 XFAILURE 1490 } 1491 1492 // remove extension from asanBin 1493 if ((getS(bin, -2) == '.') and (getS(bin, -1) == 'c')) { 1494 pErrorNULL(setS(bin, -2, 0)); 1495 } 1496 1497 if (getG(cfgD, rtBool, "print_compile_commands")) { 1498 printf("%s %s\n", asanCmd, bin); 1499 } 1500 int r; 1501 spmAssign(r, commandf("%s %s\n", asanCmd, bin)); 1502 1503 1504 if (getG(cfgD, rtBool, "clean_exe")) { 1505 if (getG(cfgD, rtBool, "print_compile_commands")) { 1506 printf("rm %s", bin); 1507 printf("\n"); 1508 } 1509 pError0(rmAll(bin)); 1510 } 1511 1512 exit(r); 1513 } 1514 } 1515 1516 1517 void listAllPackages(smallDictt *cfgD, smallArrayt *options) { 1518 1519 if (isDirG(localPackageDir)) { 1520 appendNSmashG(options, readDirDirG(rtSmallArrayt, localPackageDir)); 1521 } 1522 smallStringt *path = createS(getG(cfgD, rtChar, "system") , "/", PACKAGE_DIR); 1523 normalizePathG(path); 1524 pErrorNULL(expandHomeG(path)); 1525 if (isDirG(path)) { 1526 appendNSmashG(options, readDirDirG(rtSmallArrayt, ssGet(path))); 1527 } 1528 terminateG(path); 1529 pErrorNULL(uniqG(options, unusedV)); 1530 } 1531 1532 void showPackage(smallDictt *cfgD, smallStringt *o) { 1533 smallStringt *path = NULL; 1534 1535 // local package 1536 path = createS(localPackageDir, "/", ssGet(o), "/", PACKAGE); 1537 1538 if (fileExistsG(path)) { 1539 showPackageInfo(path); 1540 } 1541 1542 terminateG(path); 1543 1544 // global package 1545 path = createS(getG(cfgD, rtChar, "system") , "/", PACKAGE_DIR, "/", ssGet(o), "/", PACKAGE); 1546 1547 normalizePathG(path); 1548 pErrorNULL(expandHomeG(path)); 1549 if (fileExistsG(path)) { 1550 puts(BLD WHT "GLOBAL" RST); 1551 showPackageInfo(path); 1552 } 1553 1554 put 1555 terminateG(path); 1556 } 1557 1558 void showPackageInfo(smallStringt *path) { 1559 1560 smallDictt *d = loadYML(ssGet(path)); 1561 1562 smallStringt *s = getNDupG(d, rtSmallStringt, "name"); 1563 colorG(s, BLD YLW); 1564 catSG(s, ": "); 1565 1566 smallStringt *s2 = getG(d, rtSmallStringt, "version"); 1567 catG(s, s2); 1568 putsG(s); 1569 1570 terminateO(s); 1571 finishG(s2); 1572 1573 colorG(path, BLD GRN); 1574 putsG(path); 1575 1576 s = getG(d, rtSmallStringt, "bin"); 1577 if (s) { 1578 printf(BLD GRN "bin:" RST " %s\n", ssGet(s)); 1579 finishG(s); 1580 } 1581 1582 s = getG(d, rtSmallStringt, "description"); 1583 putsG(s); 1584 finishG(s); 1585 } 1586 1587 void updatePackages(bool isGlobal, smallDictt *cfgD, smallArrayt *options, bool pigz) { 1588 smallStringt *path = NULL; 1589 1590 createAllocateSmallArray(packagesToUpdate); 1591 1592 // local package 1593 if (!isGlobal) { 1594 iter(options, O) { 1595 castS(o, O); 1596 path = createS(localPackageDir, "/", ssGet(o), "/", PACKAGE); 1597 1598 // update all packages even those that are not yet installed 1599 pushG(packagesToUpdate, o); 1600 1601 terminateG(path); 1602 } 1603 } 1604 else { 1605 // global package 1606 iter(options, O) { 1607 castS(o, O); 1608 path = createS(getG(cfgD, rtChar, "system") , "/", PACKAGE_DIR, "/", ssGet(o), "/", PACKAGE); 1609 1610 normalizePathG(path); 1611 pErrorNULL(expandHomeG(path)); 1612 // update all packages even those that are not yet installed 1613 pushG(packagesToUpdate, o); 1614 1615 terminateG(path); 1616 } 1617 } 1618 1619 installPackages(isGlobal, cfgD, packagesToUpdate, pigz); 1620 smashG(packagesToUpdate); 1621 } 1622 1623 1624 void uninstallPackages(bool isGlobal, smallDictt *cfgD, smallArrayt *options) { 1625 char *cwd = NULL; 1626 1627 // choose install destination 1628 char *packageDir; 1629 if (!isGlobal) { 1630 // install package in current folder 1631 packageDir = strdup(localPackageDir); 1632 } 1633 else { 1634 // install package in system 1635 packageDir = catS(getG(cfgD, rtChar, "system") , "/", PACKAGE_DIR); 1636 } 1637 1638 pErrorNULL(iExpandHome(&packageDir)); 1639 1640 // skip when package folder is not found 1641 if (!fileExists(packageDir)) { 1642 return; 1643 } 1644 1645 cwd = getCwd(); 1646 pError0(chDir(packageDir)); 1647 1648 //logVarG(options); 1649 1650 iter(options, O) { 1651 castS(o, O); 1652 1653 // package name 1654 char *name; 1655 1656 smallArrayt *spl = splitG(o, "@");; 1657 if (lenG(spl) == 1) { 1658 name = ssGet(o); 1659 } 1660 else { 1661 name = getG(spl, rtChar, 0); 1662 } 1663 1664 if (!isDir(name)) { 1665 goto end; 1666 } 1667 1668 char *path = catS(name, "/", PACKAGE); 1669 smallDictt *d = loadYML(path); 1670 char *bin = getG(d, rtChar, "bin"); 1671 1672 // delete build files 1673 if (bin) { 1674 char *cmd = catS("sheepy -d ", bin); 1675 if (getG(cfgD, rtBool, "print_compile_commands")) { 1676 putsG(cmd); 1677 } 1678 pError0(chDir(name)); 1679 pErrorNot0(systemNFreeG(cmd)); 1680 pError0(chDir("..")); 1681 1682 // uninstall dependencies 1683 } 1684 // metapackage 1685 // uninstall dependencies globally 1686 if (!bin && isGlobal) { 1687 // install dependencies in global metapackage 1688 uninstallDependencies(cfgD, d); 1689 } 1690 1691 if (bin && isGlobal) { 1692 // unlink command in bin from package folder 1693 char *pkgBin = strdup(basename(bin)); 1694 char **rootBin_l = split(pkgBin, "."); 1695 char *rootBin = rootBin_l[0]; 1696 char *path = catS("../bin/", rootBin);; 1697 pError0(rmAll(path)); 1698 free(path); 1699 free(pkgBin); 1700 listFreeS(rootBin_l); 1701 } 1702 1703 pError0(rmAll(name)); 1704 1705 terminateG(d); 1706 free(path); 1707 1708 end: 1709 terminateG(spl); 1710 } 1711 pError0(chDir(cwd)); 1712 free(cwd); 1713 smallArrayt *arr = readDirDirO(packageDir);; 1714 if (!lenG(arr) && !isGlobal) { 1715 // remove empty shpPackages 1716 pError0(rmAll(packageDir)); 1717 } 1718 terminateG(arr); 1719 free(packageDir); 1720 } 1721 1722 void uninstallDependencies(smallDictt *cfgD, smallDictt *d) { 1723 char **cmd = NULL; 1724 1725 smallDictt *deps = getG(d, rtSmallDictt, "dependencies"); 1726 if (deps) { 1727 1728 char **depsL = keysG(deps); 1729 1730 createAllocateSmallArray(depA); 1731 fromArrayNFreeG(depA, depsL, 0); 1732 1733 uninstallPackages(params.global, cfgD, depA); 1734 terminateG(depA); 1735 finishG(deps); 1736 } 1737 } 1738 1739 1740 1741 1742 /** 1743 * spawn a child process and add to the list 1744 */ 1745 void spawnProcDeps(cbFt cb, void *arg) { 1746 pid_t pid; 1747 int status; 1748 1749 staticArrayPush(jobsDeps) 1750 staticArrayLast(jobsDeps).pid = fork(); 1751 switch (staticArrayLast(jobsDeps).pid) { 1752 case -1: 1753 perror("spawnProc error: "); 1754 XFAILURE 1755 case 0: 1756 cb(arg); 1757 perror("spawnProc error: "); 1758 XFAILURE 1759 default: 1760 break; 1761 } 1762 1763 return; 1764 } 1765 1766 /** 1767 * wait for all child processes to finish 1768 */ 1769 void waitForJobsDeps(void) { 1770 1771 range(i, staticArrayCount(jobsDeps)) { 1772 if (waitpid(staticArrayGet(jobsDeps,i).pid, &(staticArrayGet(jobsDeps,i).status), 0) == -1) { 1773 printf("couldn't wait for child %d\n", jobsDeps.list[i].pid); { 1774 XFAILURE 1775 } 1776 } 1777 1778 #ifdef __HAIKU__ 1779 if (WEXITSTATUS(staticArrayGet(jobsDeps,i).status) || WTERMSIG(staticArrayGet(jobsDeps,i).status)) { 1780 puts("subprocess trouble, check output"); 1781 XFAILURE 1782 } 1783 #else 1784 if (WEXITSTATUS(staticArrayGet(jobsDeps,i).status) || WTERMSIG(staticArrayGet(jobsDeps,i).status) || WCOREDUMP(staticArrayGet(jobsDeps,i).status)) { 1785 puts("subprocess trouble, check output"); 1786 XFAILURE 1787 } 1788 #endif 1789 } 1790 } 1791 1792 void findPrint(smallDictt *d) { 1793 1794 smallStringt *s = getNDupG(d, rtSmallStringt, "name"); 1795 colorG(s, BLD YLW); 1796 catSG(s, ": "); 1797 1798 smallStringt *s2 = getG(d, rtSmallStringt, "version"); 1799 catG(s, s2); 1800 putsG(s); 1801 1802 terminateO(s); 1803 finishG(s2); 1804 1805 printf(BLD GRN "Author: " RST "%s\n", getG(d, rtChar, "user")); 1806 printf(BLD GRN "Homepage: " RST "%s\n", getG(d, rtChar, "homepage")); 1807 printf(BLD GRN "repo: " RST "%s\n", getG(d, rtChar, "repo")); 1808 smallArrayt *a = getG(d, rtSmallArrayt, "keywords"); 1809 s = joinG(a, ", "); 1810 if (lenG(a) == 1) { 1811 printf(BLD GRN "Keyword: " RST "%s\n", ssGet(s)); 1812 } 1813 else if (lenG(a) > 1) { 1814 printf(BLD GRN "Keywords: " RST "%s\n", ssGet(s)); 1815 } 1816 finishG(s); 1817 finishG(a); 1818 1819 s = getG(d, rtSmallStringt, "description"); 1820 put 1821 putsG(s); 1822 finishG(s); 1823 } 1824 1825 1826 char **requestRegistry(smallDictt *cfgD, smallJsont *json) { 1827 1828 //TODO send the json without using the file system 1829 char *s = toStringG(json); 1830 char *reqFile = expandHomeG("~/.sheepy/api.json"); 1831 1832 writeFileG(s, reqFile); 1833 free(s); 1834 1835 char *cmd = replaceG(API_REQUEST, "$JSONFILE$", reqFile, 1); 1836 if (getG(cfgD, rtBool, "print_compile_commands")) { 1837 printf("%s\n", cmd); 1838 } 1839 char **result = execOut(cmd);; 1840 free(cmd); 1841 pError0(rmAll(reqFile)); 1842 free(reqFile); 1843 1844 return(result); 1845 } 1846 1847 void showPackageListFromRegistry(char **result, smallJsont *json) { 1848 1849 //logVarG(result); 1850 if (!result) { 1851 puts(BLD RED "Bad response from spm registry" RST); 1852 goto end; 1853 } 1854 1855 char *r = result[0]; 1856 1857 emptyG(json); 1858 parseG(json, r); 1859 listFreeS(result); 1860 //logVarG(json); 1861 //char *tmp = stringifyG(json, 4) 1862 //logVarG(tmp); 1863 //free tmp 1864 1865 if (!eqG(getTopTypeG(json), "array")) { 1866 puts(BLD RED "Bad response from spm registry" RST); 1867 goto end; 1868 } 1869 1870 // show package information 1871 smallArrayt *a = getTopG(json, rtSmallArrayt); 1872 1873 if (!lenG(a)) { 1874 puts("Empty results."); 1875 } 1876 1877 iter(a, D) { 1878 cast(smallDictt *, d, D); 1879 if (!isOTypeG(d, "smallDict")) { 1880 puts(BLD RED "Bad response from spm registry" RST); 1881 break; 1882 } 1883 findPrint(d); 1884 1885 // print -- between packages 1886 if (iterIndexG(a) != (lenG(a)-1)) { 1887 put 1888 puts("-------------"); 1889 } 1890 } 1891 1892 finishG(a); 1893 1894 end: 1895 return; 1896 } 1897 1898 void whoamiResult(char **result, smallJsont *json) { 1899 1900 //logVarG(result); 1901 1902 if (!result) { 1903 puts(BLD RED "Bad response from spm registry" RST); 1904 goto end; 1905 } 1906 1907 char *r = result[0]; 1908 emptyG(json); 1909 parseG(json, r); 1910 listFreeS(result); 1911 1912 if (!eqG(getTopTypeG(json), "array")) { 1913 puts(BLD RED "Bad response from spm registry" RST); 1914 goto end; 1915 } 1916 1917 smallArrayt *a = getTopG(json, rtSmallArrayt); 1918 1919 if (!lenG(a)) { 1920 puts("User not found."); 1921 } 1922 else { 1923 smallDictt *d = getG(a, rtSmallDictt, 0);; 1924 printf(BLD GRN "Username: " RST "%s\n", getG(d, rtChar, "username")); 1925 printf(BLD GRN "Name: " RST "%s\n", getG(d, rtChar, "name")); 1926 printf(BLD GRN "Public email: " RST "%s\n", getG(d, rtChar, "publicemail")); 1927 finishG(d); 1928 } 1929 1930 finishG(a); 1931 1932 end: 1933 return; 1934 } 1935 1936 void profileResult(char **result, smallJsont *json) { 1937 1938 //logVarG(result); 1939 1940 if (!result) { 1941 puts(BLD RED "Bad response from spm registry" RST); 1942 goto end; 1943 } 1944 1945 char *r = result[0]; 1946 emptyG(json); 1947 parseG(json, r); 1948 listFreeS(result); 1949 1950 if (!eqG(getTopTypeG(json), "array")) { 1951 puts(BLD RED "Bad response from spm registry" RST); 1952 goto end; 1953 } 1954 1955 smallArrayt *a = getTopG(json, rtSmallArrayt); 1956 1957 if (!lenG(a)) { 1958 puts("No user found."); 1959 } 1960 else { 1961 iter(a, D) { 1962 cast(smallDictt*, d, D); 1963 printf(BLD GRN "Username: " RST "%s\n", getG(d, rtChar, "username")); 1964 printf(BLD GRN "Name: " RST "%s\n", getG(d, rtChar, "name")); 1965 printf(BLD GRN "Public email: " RST "%s\n", getG(d, rtChar, "publicemail")); 1966 1967 smallArrayt *pkgs = getG(d, rtSmallArrayt, "packages"); 1968 1969 if (!pkgs) { 1970 goto nullpkgs; 1971 } 1972 1973 if (!lenG(pkgs)) { 1974 goto nopkgs; 1975 } 1976 1977 sortG(pkgs); 1978 char *s = joinSG(pkgs, " ");; 1979 1980 if (lenG(pkgs) == 1) { 1981 printf(BLD GRN "Package: " RST "%s\n", s); 1982 } 1983 else { 1984 printf(BLD GRN "Packages: " RST "%s\n", s); 1985 } 1986 1987 free(s); 1988 1989 nopkgs: 1990 finishG(pkgs); 1991 1992 nullpkgs: 1993 // print -- between packages 1994 if (iterIndexG(a) != (lenG(a)-1)) { 1995 put 1996 puts("-------------"); 1997 } 1998 } 1999 2000 finishG(a); 2001 2002 end: 2003 return; 2004 } 2005 } 2006 2007 void unpublishResult(char **result, smallJsont *json) { 2008 2009 //logVarG(result); 2010 2011 if (!result) { 2012 puts(BLD RED "Bad response from spm registry" RST); 2013 goto end; 2014 } 2015 2016 char *r = result[0]; 2017 emptyG(json); 2018 parseG(json, r); 2019 listFreeS(result); 2020 2021 if (!eqG(getTopTypeG(json), "array")) { 2022 puts(BLD RED "Bad response from spm registry" RST); 2023 goto end; 2024 } 2025 2026 smallArrayt *a = getTopG(json, rtSmallArrayt); 2027 2028 if (!lenG(a)) { 2029 puts("Nothing unpublished."); 2030 } 2031 else { 2032 char *unpub = joinSG(a, ", "); 2033 printf("Unpublished: %s\n", unpub); 2034 free(unpub); 2035 } 2036 2037 finishG(a); 2038 2039 end: 2040 return; 2041 } 2042 2043 void apikeyResult(char **result, smallJsont *json) { 2044 2045 //logVarG(result); 2046 2047 if (!result) { 2048 puts(BLD RED "Bad response from spm registry" RST); 2049 goto end; 2050 } 2051 2052 char *r = result[0]; 2053 emptyG(json); 2054 parseG(json, r); 2055 listFreeS(result); 2056 2057 if (!eqG(getTopTypeG(json), "array")) { 2058 puts(BLD RED "Bad response from spm registry" RST); 2059 goto end; 2060 } 2061 2062 smallArrayt *a = getTopG(json, rtSmallArrayt); 2063 2064 if (!lenG(a)) { 2065 puts("Error: API key not found."); 2066 } 2067 else { 2068 printf("API Key: %s\n", getG(a, rtChar, 0)); 2069 } 2070 2071 finishG(a); 2072 end: 2073 return; 2074 } 2075 2076 void addUserResult(char **result, smallJsont *json) { 2077 2078 //logVarG(result); 2079 2080 if (!result) { 2081 puts(BLD RED "Bad response from spm registry" RST); 2082 goto end; 2083 } 2084 2085 char *r = result[0]; 2086 emptyG(json); 2087 parseG(json, r); 2088 listFreeS(result); 2089 2090 if (!eqG(getTopTypeG(json), "array")) { 2091 puts(BLD RED "Bad response from spm registry" RST); 2092 goto end; 2093 } 2094 2095 smallArrayt *a = getTopG(json, rtSmallArrayt); 2096 2097 if (!lenG(a)) { 2098 puts(BLD RED "Bad response from spm registry" RST); 2099 goto finisha; 2100 } 2101 2102 if (isBlankG(getG(a, rtChar, 0))) { 2103 puts(BLD RED "Error: User not created." RST); 2104 printf(BLD RED "%s" RST, getG(a, rtChar, 1)); 2105 } 2106 else { 2107 puts(BLD GRN "New user created successfully." RST); 2108 } 2109 2110 finisha: 2111 finishG(a); 2112 2113 end: 2114 return; 2115 } 2116 2117 // vim: set expandtab ts=2 sw=2: