sheepy

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

common.c (6809B)


      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 #include "libsheepyObject.h"
     24 #include "common.h"
     25 
     26 #define internal static
     27 
     28 #include <stdio.h>
     29 #include <dirent.h>
     30 #include <string.h>
     31 #include <sys/wait.h>
     32 #include <stdlib.h>
     33 #include <unistd.h>
     34 
     35 #if (!(__arm__ || __APPLE__ || __FreeBSD__ || __TERMUX__ || __OpenBSD__ || __DragonFly__ || MUSL_LIBC || __sun__))
     36 #elif (__TERMUX__)
     37 #elif ((__HAIKU__))
     38 #else
     39 #endif
     40 smallJsont *loadSheepyConfig(char **homedir);
     41 void sourceFromTemplate(const char *filename);
     42 void pkgFromTemplate(const char *pkgname);
     43 void spawnProc(cbFt cb, void *arg);
     44 void waitForJobs(void);
     45 void childProc(void *arg);
     46 char* findLocalPackageDir(smallDictt* cfgD);
     47 
     48 /**
     49  * \file
     50  * common functions and data
     51  */
     52 
     53 #if (!(__arm__ || __APPLE__ || __FreeBSD__ || __TERMUX__ || __OpenBSD__ || __DragonFly__ || MUSL_LIBC || __sun__))
     54 char *defaultSpC = CONFIG_YML;
     55 #elif (__TERMUX__)
     56 // gold doesn't work in raspbian - deactivate
     57 // define __arm__
     58 char *defaultSpC = CONFIG_YML_NO_GOLD_ARM;
     59 #elif ((__HAIKU__))
     60 // gold doesn't work in raspbian - deactivate
     61 // remove -rdynamic
     62 // install in /boot/home/config/non-packaged
     63 char *defaultSpC = CONFIG_YML_HAIKU;
     64 #else
     65 // gold doesn't work in raspbian - deactivate
     66 char *defaultSpC = CONFIG_YML_NO_GOLD;
     67 #endif
     68 
     69 //^^ __arm__
     70 /**
     71  * child process list compiling the code
     72  */
     73 jobst jobs;
     74 
     75 /**
     76  * load ~/.sheepy/config.yml
     77  * if config.yml doesn't exists, create a default config
     78  *
     79  * \return
     80  *  homedir real sheepy home path
     81  *  json configuration
     82  */
     83 smallJsont *loadSheepyConfig(char **homedir) {
     84   char *dum = NULL;
     85 
     86   // Steps
     87   // create homedir and default configuration
     88   // load sheepy configuration
     89 
     90   // create homedir
     91   *homedir   = expandHomeG(HOME_DIR);
     92   char *file = catS(*homedir, CONFIG_NAME);
     93   if (!fileExists(*homedir) || !fileExists(file)) {
     94     pError0(mkdirParents(*homedir));
     95     pError0(writeFileG(defaultSpC, file));
     96   }
     97 
     98   // load sheepy configuration
     99   createAllocateSmallJson(cfgJ);
    100   pErrorNULL(readFileG(cfgJ, file));
    101   free(file);
    102   return(cfgJ);
    103 }
    104 
    105 void sourceFromTemplate(const char *filename) {
    106 
    107   if (!filename || isBlankG(filename)) {
    108     return;
    109   }
    110 
    111   char *fn;
    112   if (endsWithG(filename, ".c")) {
    113     fn = strdup(filename);
    114   }
    115   else {
    116     fn = appendG(filename, ".c");
    117   }
    118 
    119   if (fileExists(fn)) {
    120     printf(BLD RED "%s already exists. Current path: ", fn);
    121     char *s = getCwd();;
    122     logNFree(s);
    123     puts(RST);
    124     free(fn);
    125     return;
    126   }
    127 
    128   pError0(writeFileG(C_TEMPLATE, fn));
    129   pError0(fileChmod(fn, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH));
    130 
    131   free(fn);
    132 }
    133 
    134 
    135 
    136 void pkgFromTemplate(const char *pkgname) {
    137 
    138   if (!pkgname || isBlankG(pkgname)) {
    139     return;
    140   }
    141 
    142   if (fileExists(PACKAGE)) {
    143     printf(BLD RED "%s already exists. Current path: ", PACKAGE);
    144     char *s = getCwd();;
    145     logNFree(s);
    146     puts(RST);
    147     return;
    148   }
    149 
    150   char *testname = dupG(pkgname);;
    151   toUpper(testname[0]);
    152   char *memcheckname = dupG(testname);;
    153   char *asanname = dupG(testname);;
    154   pErrorNULL(iPrependS(&testname, "test"));
    155   pErrorNULL(iPrependS(&memcheckname, "memcheck"));
    156   pErrorNULL(iPrependS(&asanname, "asan"));
    157 
    158   char *t = replaceManyS(PKG_TEMPLATE, "<PKG_NAME>", pkgname, "<TEST_NAME>", testname, "<MEMCHECK_NAME>", memcheckname, "<ASAN_NAME>", asanname);;
    159 
    160   writeFileG(t, PACKAGE);
    161   freeManyS(testname, memcheckname, asanname, t);
    162 }
    163 
    164 /**
    165  * spawn a child process and add to the list
    166  */
    167 void spawnProc(cbFt cb, void *arg) {
    168   pid_t pid;
    169   int status;
    170 
    171   staticArrayPush(jobs)
    172   staticArrayLast(jobs).pid = fork();
    173   switch (staticArrayLast(jobs).pid) {
    174     case -1:
    175       perror("spawnProc error: ");
    176       XFAILURE
    177     case 0:
    178       cb(arg);
    179       perror("spawnProc error: ");
    180       XFAILURE
    181     default:
    182       break;
    183   }
    184 
    185   return;
    186 }
    187 
    188 /**
    189  * wait for all child processes to finish
    190  */
    191 void waitForJobs(void) {
    192 
    193   range(i, staticArrayCount(jobs)) {
    194     if (waitpid(staticArrayGet(jobs,i).pid, &(staticArrayGet(jobs,i).status), 0) == -1) {
    195       printf("couldn't wait for child %d\n", jobs.list[i].pid); {
    196       XFAILURE
    197     }
    198       }
    199 
    200     #ifdef __HAIKU__
    201     if (WEXITSTATUS(staticArrayGet(jobs,i).status) || WTERMSIG(staticArrayGet(jobs,i).status)) {
    202       puts("subprocess trouble, check output");
    203       XFAILURE
    204     }
    205     #else
    206     if (WEXITSTATUS(staticArrayGet(jobs,i).status) || WTERMSIG(staticArrayGet(jobs,i).status) || WCOREDUMP(staticArrayGet(jobs,i).status)) {
    207       puts("subprocess trouble, check output");
    208       XFAILURE
    209     }
    210     #endif
    211   }
    212 }
    213 
    214 void childProc(void *arg) {
    215   char **args = NULL;
    216   char **userArgs = NULL;
    217 
    218   userArgs = (char **) arg;
    219 
    220   //listPrintS(userArgs);
    221 
    222   execvp(userArgs[0], userArgs);
    223   perror("failed to start: ");
    224   puts(userArgs[0]);
    225   XFAILURE
    226 }
    227 
    228 
    229 char* findLocalPackageDir(smallDictt* cfgD) {
    230 
    231   char *localPackageDir = PACKAGE_DIR;
    232 
    233   if (!isDirG(PACKAGE_DIR)) {
    234     // search in parent directories
    235     char *cwd  = getCwd();
    236     char **spl = splitG(cwd, '/');
    237     while (!isBlankG(spl)) {
    238       char *p = joinG(spl, '/');;
    239       pErrorNULL(iAppendManyS(&p, "/", PACKAGE_DIR));
    240       if (isDir(p)) {
    241         localPackageDir = p;
    242         break;
    243       }
    244       //logVarG(p);
    245       free(p);
    246       pErrorNULL(delG(&spl, -1, 0));
    247     }
    248     freeG(spl);
    249     free(cwd);
    250 
    251     // ignore found dir if it is the global package dir
    252     char *packageDir = catS(getG(cfgD, rtChar, "system") , "/", PACKAGE_DIR);
    253     pErrorNULL(normalizePathG(&packageDir));
    254     pErrorNULL(expandHomeG(&packageDir));
    255     if (eqG(localPackageDir, packageDir)) {
    256       // revert to default package dir, the local package dir cant be the global package dir
    257       free(localPackageDir);
    258       localPackageDir = PACKAGE_DIR;
    259     }
    260     free(packageDir);
    261   }
    262   return(localPackageDir);
    263 }