Coverage Report

Created: 2024-09-11 06:14

/src/clib/src/clib-configure.c
Line
Count
Source (jump to first uncovered line)
1
//
2
// clib-configure.c
3
//
4
// Copyright (c) 2012-2021 clib authors
5
// MIT licensed
6
//
7
8
#include <curl/curl.h>
9
#include <errno.h>
10
#include <libgen.h>
11
#include <limits.h>
12
#include <stdio.h>
13
#include <string.h>
14
15
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
16
#include <unistd.h>
17
#endif
18
19
#ifdef HAVE_PTHREADS
20
#include <pthread.h>
21
#endif
22
23
#ifdef _WIN32
24
#include <direct.h>
25
#define getcwd _getcwd
26
#else
27
#include <unistd.h>
28
#endif
29
30
#include "common/clib-cache.h"
31
#include "common/clib-package.h"
32
#include "common/clib-settings.h"
33
34
#include <asprintf/asprintf.h>
35
#include <commander/commander.h>
36
#include <debug/debug.h>
37
#include <fs/fs.h>
38
#include <hash/hash.h>
39
#include <list/list.h>
40
#include <logger/logger.h>
41
#include <path-join/path-join.h>
42
#include <str-flatten/str-flatten.h>
43
#include <trim/trim.h>
44
45
#include "version.h"
46
47
0
#define PROGRAM_NAME "clib-configure"
48
49
#define SX(s) #s
50
#define S(s) SX(s)
51
52
#if defined(_WIN32) || defined(WIN32) || defined(__MINGW32__) ||               \
53
    defined(__MINGW64__)
54
#define setenv(k, v, _) _putenv_s(k, v)
55
#define realpath(a, b) _fullpath(a, b, strlen(a))
56
#endif
57
58
typedef struct options options_t;
59
struct options {
60
  const char *dir;
61
  char *prefix;
62
  int force;
63
  int verbose;
64
  int dev;
65
  int skip_cache;
66
  int flags;
67
  int global;
68
#ifdef HAVE_PTHREADS
69
  unsigned int concurrency;
70
#endif
71
};
72
73
clib_package_opts_t package_opts = {0};
74
clib_package_t *root_package = 0;
75
76
hash_t *configured = 0;
77
command_t program = {0};
78
debug_t debugger = {0};
79
80
char **rest_argv = 0;
81
int rest_offset = 0;
82
int rest_argc = 0;
83
84
options_t opts = {.skip_cache = 0,
85
                  .verbose = 1,
86
                  .force = 0,
87
                  .dev = 0,
88
#ifdef HAVE_PTHREADS
89
                  .concurrency = MAX_THREADS,
90
#endif
91
92
#ifdef _WIN32
93
                  .dir = ".\\deps"
94
#else
95
                  .dir = "./deps"
96
#endif
97
98
};
99
100
int configure_package(const char *dir);
101
102
#ifdef HAVE_PTHREADS
103
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
104
typedef struct clib_package_thread clib_package_thread_t;
105
struct clib_package_thread {
106
  const char *dir;
107
};
108
109
0
void *configure_package_with_manifest_name_thread(void *arg) {
110
0
  clib_package_thread_t *wrap = arg;
111
0
  const char *dir = wrap->dir;
112
0
  configure_package(dir);
113
0
  return 0;
114
0
}
115
#endif
116
117
0
int configure_package_with_manifest_name(const char *dir, const char *file) {
118
0
  clib_package_t *package = 0;
119
0
  char *json = NULL;
120
0
  int ok = 0;
121
0
  int rc = 0;
122
123
0
#ifdef PATH_MAX
124
0
  long path_max = PATH_MAX;
125
#elif defined(_PC_PATH_MAX)
126
  long path_max = pathconf(dir, _PC_PATH_MAX);
127
#else
128
  long path_max = 4096;
129
#endif
130
131
0
  char *path = path_join(dir, file);
132
133
0
  if (0 == path) {
134
0
    return -ENOMEM;
135
0
  }
136
137
0
#ifdef HAVE_PTHREADS
138
0
  pthread_mutex_lock(&mutex);
139
0
#endif
140
141
0
  if (!root_package) {
142
0
    const char *name = NULL;
143
0
    unsigned int i = 0;
144
145
0
    do {
146
0
      name = manifest_names[i];
147
0
      json = fs_read(name);
148
0
    } while (NULL != manifest_names[++i] && !json);
149
150
0
    if (json) {
151
0
      root_package = clib_package_new(json, opts.verbose);
152
0
    }
153
154
0
    if (root_package && root_package->prefix) {
155
0
      char prefix[path_max];
156
0
      memset(prefix, 0, path_max);
157
0
      realpath(root_package->prefix, prefix);
158
0
      unsigned long int size = strlen(prefix) + 1;
159
0
      free(root_package->prefix);
160
0
      root_package->prefix = malloc(size);
161
0
      memset((void *)root_package->prefix, 0, size);
162
0
      memcpy((void *)root_package->prefix, prefix, size);
163
0
    }
164
0
  }
165
166
0
  if (hash_has(configured, path)) {
167
0
#ifdef HAVE_PTHREADS
168
0
    pthread_mutex_unlock(&mutex);
169
0
#endif
170
0
    goto cleanup;
171
0
  }
172
173
0
#ifdef HAVE_PTHREADS
174
0
  pthread_mutex_unlock(&mutex);
175
0
#endif
176
177
  // Free the json if it was allocated before attempting to modify it
178
0
  free(json);
179
0
  json = NULL; 
180
181
0
  if (0 == fs_exists(path)) {
182
0
    debug(&debugger, "read %s", path);
183
0
    json = fs_read(path);
184
0
  }
185
186
0
  if (0 != json) {
187
#ifdef DEBUG
188
    package = clib_package_new(json, 1);
189
#else
190
0
    package = clib_package_new(json, 0);
191
0
#endif
192
0
  } else {
193
#ifdef DEBUG
194
    package = clib_package_new_from_slug(dir, 1);
195
#else
196
0
    package = clib_package_new_from_slug(dir, 0);
197
0
#endif
198
0
  }
199
200
0
  if (0 == package) {
201
0
    rc = -ENOMEM;
202
0
    goto cleanup;
203
0
  }
204
205
0
  if (0 != package->flags && opts.flags) {
206
0
#ifdef HAVE_PTHREADS
207
0
    rc = pthread_mutex_lock(&mutex);
208
0
#endif
209
210
0
    hash_set(configured, path, "t");
211
0
    ok = 1;
212
0
    fprintf(stdout, "%s ", trim(package->flags));
213
0
    fflush(stdout);
214
0
  } else if (0 != package->configure) {
215
0
    char *command = 0;
216
0
    char *args = rest_argc > 0
217
0
                     ? str_flatten((const char **)rest_argv, 0, rest_argc)
218
0
                     : "";
219
220
0
    asprintf(&command, "cd %s && %s %s", dir, package->configure, args);
221
222
0
    if (root_package && root_package->prefix) {
223
0
      package_opts.prefix = root_package->prefix;
224
0
      clib_package_set_opts(package_opts);
225
0
      setenv("PREFIX", package_opts.prefix, 1);
226
0
    } else if (opts.prefix) {
227
0
      setenv("PREFIX", opts.prefix, 1);
228
0
    } else if (package->prefix) {
229
0
      char prefix[path_max];
230
0
      memset(prefix, 0, path_max);
231
0
      realpath(package->prefix, prefix);
232
0
      unsigned long int size = strlen(prefix) + 1;
233
0
      free(package->prefix);
234
0
      package->prefix = malloc(size);
235
0
      memset((void *)package->prefix, 0, size);
236
0
      memcpy((void *)package->prefix, prefix, size);
237
0
      setenv("PREFIX", package->prefix, 1);
238
0
    }
239
240
0
    if (rest_argc > 0) {
241
0
      free(args);
242
0
    }
243
244
0
    if (0 != opts.verbose) {
245
0
      logger_warn("configure", "%s: %s", package->name, package->configure);
246
0
    }
247
248
0
    debug(&debugger, "system: %s", command);
249
0
    rc = system(command);
250
0
    free(command);
251
0
    command = 0;
252
0
#ifdef HAVE_PTHREADS
253
0
    rc = pthread_mutex_lock(&mutex);
254
0
#endif
255
256
0
    hash_set(configured, path, "t");
257
0
    ok = 1;
258
0
  } else {
259
0
#ifdef HAVE_PTHREADS
260
0
    rc = pthread_mutex_lock(&mutex);
261
0
#endif
262
263
0
    hash_set(configured, path, "f");
264
0
    ok = 1;
265
0
  }
266
267
0
  if (0 != rc) {
268
0
    goto cleanup;
269
0
  }
270
271
0
#ifdef HAVE_PTHREADS
272
0
  pthread_mutex_unlock(&mutex);
273
0
#endif
274
275
0
  if (0 != package->dependencies) {
276
0
    list_iterator_t *iterator = 0;
277
0
    list_node_t *node = 0;
278
279
0
#ifdef HAVE_PTHREADS
280
0
    clib_package_thread_t wraps[opts.concurrency];
281
0
    pthread_t threads[opts.concurrency];
282
0
    unsigned int i = 0;
283
0
#endif
284
285
0
    iterator = list_iterator_new(package->dependencies, LIST_HEAD);
286
287
0
    while ((node = list_iterator_next(iterator))) {
288
0
      clib_package_dependency_t *dep = node->val;
289
0
      char *slug = 0;
290
0
      asprintf(&slug, "%s/%s@%s", dep->author, dep->name, dep->version);
291
292
0
      clib_package_t *dependency = clib_package_new_from_slug(slug, 0);
293
0
      char *dep_dir = path_join(opts.dir, dependency->name);
294
295
0
      free(slug);
296
0
      clib_package_free(dependency);
297
298
0
#ifdef HAVE_PTHREADS
299
0
      clib_package_thread_t *wrap = &wraps[i];
300
0
      pthread_t *thread = &threads[i];
301
0
      wrap->dir = dep_dir;
302
0
      rc = pthread_create(thread, 0,
303
0
                          configure_package_with_manifest_name_thread, wrap);
304
305
0
      if (++i >= opts.concurrency) {
306
0
        for (int j = 0; j < i; ++j) {
307
0
          pthread_join(threads[j], 0);
308
0
          free((void *)wraps[j].dir);
309
0
        }
310
311
0
        i = 0;
312
0
      }
313
0
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
314
0
      if (!opts.flags) {
315
0
        usleep(1024 * 10);
316
0
      }
317
0
#endif
318
#else
319
      if (0 == dep_dir) {
320
        rc = -ENOMEM;
321
        goto cleanup;
322
      }
323
324
      rc = configure_package(dep_dir);
325
326
      free((void *)dep_dir);
327
328
      if (0 != rc) {
329
        goto cleanup;
330
      }
331
#endif
332
0
    }
333
334
0
#ifdef HAVE_PTHREADS
335
0
    for (int j = 0; j < i; ++j) {
336
0
      pthread_join(threads[j], 0);
337
0
      free((void *)wraps[j].dir);
338
0
    }
339
0
#endif
340
341
0
    if (0 != iterator) {
342
0
      list_iterator_destroy(iterator);
343
0
    }
344
0
  }
345
346
0
  if (opts.dev && 0 != package->development) {
347
0
    list_iterator_t *iterator = 0;
348
0
    list_node_t *node = 0;
349
350
0
#ifdef HAVE_PTHREADS
351
0
    clib_package_thread_t wraps[opts.concurrency];
352
0
    pthread_t threads[opts.concurrency];
353
0
    unsigned int i = 0;
354
0
#endif
355
356
0
    iterator = list_iterator_new(package->development, LIST_HEAD);
357
358
0
    while ((node = list_iterator_next(iterator))) {
359
0
      clib_package_dependency_t *dep = node->val;
360
0
      char *slug = 0;
361
0
      asprintf(&slug, "%s/%s@%s", dep->author, dep->name, dep->version);
362
363
0
      clib_package_t *dependency = clib_package_new_from_slug(slug, 0);
364
0
      char *dep_dir = path_join(opts.dir, dependency->name);
365
366
0
      free(slug);
367
0
      clib_package_free(dependency);
368
369
0
#ifdef HAVE_PTHREADS
370
0
      clib_package_thread_t *wrap = &wraps[i];
371
0
      pthread_t *thread = &threads[i];
372
0
      wrap->dir = dep_dir;
373
0
      rc = pthread_create(thread, 0,
374
0
                          configure_package_with_manifest_name_thread, wrap);
375
376
0
      if (++i >= opts.concurrency) {
377
0
        for (int j = 0; j < i; ++j) {
378
0
          pthread_join(threads[j], 0);
379
0
          free((void *)wraps[j].dir);
380
0
        }
381
382
0
        i = 0;
383
0
      }
384
0
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
385
0
      if (!opts.flags) {
386
0
        usleep(1024 * 10);
387
0
      }
388
0
#endif
389
#else
390
      if (0 == dep_dir) {
391
        rc = -ENOMEM;
392
        goto cleanup;
393
      }
394
395
      rc = configure_package(dep_dir);
396
397
      free((void *)dep_dir);
398
399
      if (0 != rc) {
400
        goto cleanup;
401
      }
402
#endif
403
0
    }
404
405
0
#ifdef HAVE_PTHREADS
406
0
    for (int j = 0; j < i; ++j) {
407
0
      pthread_join(threads[j], 0);
408
0
      free((void *)wraps[j].dir);
409
0
    }
410
0
#endif
411
412
0
    if (0 != iterator) {
413
0
      list_iterator_destroy(iterator);
414
0
    }
415
0
  }
416
417
0
cleanup:
418
0
  if (0 != package) {
419
0
    clib_package_free(package);
420
0
  }
421
0
  if (0 != json) {
422
0
    free(json);
423
0
  }
424
0
  if (0 == ok) {
425
0
    if (0 != path) {
426
0
      free(path);
427
0
    }
428
0
  }
429
0
  return rc;
430
0
}
431
432
0
int configure_package(const char *dir) {
433
0
  const char *name = NULL;
434
0
  unsigned int i = 0;
435
0
  int rc = 0;
436
437
0
  do {
438
0
    name = manifest_names[i];
439
0
    rc = configure_package_with_manifest_name(dir, name);
440
0
  } while (NULL != manifest_names[++i] && 0 != rc);
441
442
0
  return rc;
443
0
}
444
445
0
static void setopt_skip_cache(command_t *self) {
446
0
  opts.skip_cache = 1;
447
0
  debug(&debugger, "set skip cache flag");
448
0
}
449
450
0
static void setopt_dev(command_t *self) {
451
0
  opts.dev = 1;
452
0
  debug(&debugger, "set dev flag");
453
0
}
454
455
0
static void setopt_force(command_t *self) {
456
0
  opts.force = 1;
457
0
  debug(&debugger, "set force flag");
458
0
}
459
460
0
static void setopt_global(command_t *self) {
461
0
  opts.global = 1;
462
0
  debug(&debugger, "set global flag");
463
0
}
464
465
0
static void setopt_flags(command_t *self) {
466
0
  opts.flags = 1;
467
0
  opts.verbose = 0;
468
0
  debug(&debugger, "set flags flag");
469
0
}
470
471
0
static void setopt_prefix(command_t *self) {
472
0
  opts.prefix = (char *)self->arg;
473
0
  debug(&debugger, "set prefix: %s", opts.prefix);
474
0
}
475
476
0
static void setopt_dir(command_t *self) {
477
0
  opts.dir = (char *)self->arg;
478
0
  debug(&debugger, "set dir: %s", opts.dir);
479
0
}
480
481
0
static void setopt_quiet(command_t *self) {
482
0
  opts.verbose = 0;
483
0
  debug(&debugger, "set quiet flag");
484
0
}
485
486
#ifdef HAVE_PTHREADS
487
0
static void setopt_concurrency(command_t *self) {
488
0
  if (self->arg) {
489
0
    opts.concurrency = atol(self->arg);
490
0
    debug(&debugger, "set concurrency: %lu", opts.concurrency);
491
0
  }
492
0
}
493
#endif
494
495
0
int main2(int argc, char **argv) {
496
0
  int rc = 0;
497
498
0
#ifdef PATH_MAX
499
0
  long path_max = PATH_MAX;
500
#elif defined(_PC_PATH_MAX)
501
  long path_max = pathconf(opts.dir, _PC_PATH_MAX);
502
#else
503
  long path_max = 4096;
504
#endif
505
506
0
  char CWD[path_max];
507
508
0
  memset(CWD, 0, path_max);
509
510
0
  if (0 == getcwd(CWD, path_max)) {
511
0
    return -errno;
512
0
  }
513
514
0
  configured = hash_new();
515
0
  hash_set(configured, strdup("__" PROGRAM_NAME "__"), CLIB_VERSION);
516
517
0
  command_init(&program, PROGRAM_NAME, CLIB_VERSION);
518
0
  debug_init(&debugger, PROGRAM_NAME);
519
520
0
  program.usage = "[options] [name ...]";
521
522
0
  command_option(&program, "-o", "--out <dir>",
523
0
                 "change the output directory [deps]", setopt_dir);
524
525
0
  command_option(&program, "-P", "--prefix <dir>",
526
0
                 "change the prefix directory (usually '/usr/local')",
527
0
                 setopt_prefix);
528
529
0
  command_option(&program, "-q", "--quiet", "disable verbose output",
530
0
                 setopt_quiet);
531
532
0
  command_option(&program, "-d", "--dev", "configure development dependencies",
533
0
                 setopt_dev);
534
535
0
  command_option(&program, "-f", "--force",
536
0
                 "force the action of something, like overwriting a file",
537
0
                 setopt_force);
538
539
0
  command_option(&program, "--cflags", "--flags",
540
0
                 "output compiler flags instead of configuring", setopt_flags);
541
542
0
  command_option(&program, "-c", "--skip-cache", "skip cache when configuring",
543
0
                 setopt_skip_cache);
544
545
0
#ifdef HAVE_PTHREADS
546
0
  command_option(&program, "-C", "--concurrency <number>",
547
0
                 "Set concurrency (default: " S(MAX_THREADS) ")",
548
0
                 setopt_concurrency);
549
0
#endif
550
551
0
  command_parse(&program, argc, argv);
552
553
0
  if (opts.dir) {
554
0
    char dir[path_max];
555
0
    memset(dir, 0, path_max);
556
0
    realpath(opts.dir, dir);
557
0
    unsigned long int size = strlen(dir) + 1;
558
0
    opts.dir = malloc(size);
559
0
    memset((void *)opts.dir, 0, size);
560
0
    memcpy((void *)opts.dir, dir, size);
561
0
  }
562
563
0
  if (opts.prefix) {
564
0
    char prefix[path_max];
565
0
    memset(prefix, 0, path_max);
566
0
    realpath(opts.prefix, prefix);
567
0
    unsigned long int size = strlen(prefix) + 1;
568
0
    opts.prefix = malloc(size);
569
0
    memset((void *)opts.prefix, 0, size);
570
0
    memcpy((void *)opts.prefix, prefix, size);
571
0
  }
572
573
0
  rest_offset = program.argc;
574
575
0
  if (argc > 0) {
576
0
    int rest = 0;
577
0
    int i = 0;
578
0
    do {
579
0
      char *arg = program.nargv[i];
580
0
      if (arg && '-' == arg[0] && '-' == arg[1] && 2 == strlen(arg)) {
581
0
        rest = 1;
582
0
        rest_offset = i + 1;
583
0
      } else if (arg && rest) {
584
0
        (void)rest_argc++;
585
0
      }
586
0
    } while (program.nargv[++i]);
587
0
  }
588
589
0
  if (rest_argc > 0) {
590
0
    rest_argv = malloc(rest_argc * sizeof(char *));
591
0
    memset(rest_argv, 0, rest_argc * sizeof(char *));
592
593
0
    int j = 0;
594
0
    int i = rest_offset;
595
0
    do {
596
0
      rest_argv[j++] = program.nargv[i++];
597
0
    } while (program.nargv[i]);
598
0
  }
599
600
0
  if (0 != curl_global_init(CURL_GLOBAL_ALL)) {
601
0
    logger_error("error", "Failed to initialize cURL");
602
0
    return 1;
603
0
  }
604
605
0
  clib_cache_init(CLIB_PACKAGE_CACHE_TIME);
606
607
0
  package_opts.skip_cache = opts.skip_cache;
608
0
  package_opts.prefix = opts.prefix;
609
0
  package_opts.global = opts.global;
610
0
  package_opts.force = opts.force;
611
612
0
  clib_package_set_opts(package_opts);
613
614
0
  if (0 == program.argc || (argc == rest_offset + rest_argc)) {
615
0
    rc = configure_package(CWD);
616
0
  } else {
617
0
    for (int i = 1; i <= rest_offset; ++i) {
618
0
      char *dep = program.nargv[i];
619
620
0
      if ('.' == dep[0]) {
621
0
        char dir[path_max];
622
0
        memset(dir, 0, path_max);
623
0
        dep = realpath(dep, dir);
624
0
      } else {
625
0
        fs_stats *stats = fs_stat(dep);
626
0
        if (!stats) {
627
0
          dep = path_join(opts.dir, dep);
628
0
        } else {
629
0
          free(stats);
630
0
        }
631
0
      }
632
633
0
      fs_stats *stats = fs_stat(dep);
634
635
0
      if (stats && (S_IFREG == (stats->st_mode & S_IFMT)
636
0
#if defined(__unix__) || defined(__linux__) || defined(_POSIX_VERSION)
637
0
                    || S_IFLNK == (stats->st_mode & S_IFMT)
638
0
#endif
639
0
                        )) {
640
0
        dep = basename(dep);
641
0
        rc = configure_package_with_manifest_name(dirname(dep), basename(dep));
642
0
      } else {
643
0
        rc = configure_package(dep);
644
645
        // try with slug
646
0
        if (0 != rc) {
647
0
          rc = configure_package(program.nargv[i]);
648
0
        }
649
0
      }
650
651
0
      if (stats) {
652
0
        free(stats);
653
0
        stats = 0;
654
0
      }
655
0
    }
656
0
  }
657
658
0
  int total_configured = 0;
659
0
  hash_each(configured, {
660
0
    if (0 == strncmp("t", val, 1)) {
661
0
      (void)total_configured++;
662
0
    }
663
0
    if (0 != key) {
664
0
      free((void *)key);
665
0
    }
666
0
  });
667
668
0
  hash_free(configured);
669
0
  command_free(&program);
670
0
  curl_global_cleanup();
671
0
  clib_package_cleanup();
672
673
0
  if (opts.dir) {
674
0
    free((void *)opts.dir);
675
0
  }
676
677
0
  if (opts.prefix) {
678
0
    free(opts.prefix);
679
0
  }
680
681
0
  if (rest_argc > 0) {
682
0
    free(rest_argv);
683
0
    rest_offset = 0;
684
0
    rest_argc = 0;
685
0
    rest_argv = 0;
686
0
  }
687
688
0
  if (0 == rc) {
689
0
    if (opts.flags && total_configured > 0) {
690
0
      printf("\n");
691
0
    }
692
693
0
    if (opts.verbose) {
694
0
      if (total_configured > 1) {
695
0
        logger_info("info", "configured %d packages", total_configured);
696
0
      } else if (1 == total_configured) {
697
0
        logger_info("info", "configured 1 package");
698
0
      } else {
699
0
        logger_info("info", "configured 0 packages");
700
0
      }
701
0
    }
702
0
  }
703
704
0
  return rc;
705
0
}