#!/bin/sh # MIT License # Autark (https://autark.dev) build system script wrapper. # Copyright (c) 2012-2026 Softmotions Ltd # Autark: aec5320de2e44ef5a0338f9ea990ed2a # https://github.com/Softmotions/autark META_VERSION=0.9.8 META_REVISION=b82a078 cd "$(cd "$(dirname "$0")"; pwd -P)" prev_arg="" for arg in "$@"; do case "$prev_arg" in -H) AUTARK_HOME="${arg}/.autark-dist" prev_arg="" # сброс continue ;; esac case "$arg" in -H) prev_arg="-H" ;; -H*) AUTARK_HOME="${arg#-H}/.autark-dist" ;; --cache=*) AUTARK_HOME="${arg#--cache=}/.autark-dist" ;; *) prev_arg="" ;; esac done export AUTARK_HOME=${AUTARK_HOME:-autark-cache/.autark-dist} AUTARK=${AUTARK_HOME}/autark if [ "${META_VERSION}.${META_REVISION}" = "$(${AUTARK} -v)" ]; then ${AUTARK} "$@" exit $? fi set -e echo "Building Autark executable in ${AUTARK_HOME}" if [ -n "$CC" ] && command -v "$CC" >/dev/null 2>&1; then COMPILER="$CC" elif command -v clang >/dev/null 2>&1; then COMPILER=clang elif command -v gcc >/dev/null 2>&1; then COMPILER=gcc else echo "No suitable C compiler found" >&2 exit 1 fi COPTS="-O1" if ${COMPILER} --version | grep -i -E 'clang|gcc'; then COPTS="--std=c99 -O1 -march=native " fi mkdir -p ${AUTARK_HOME} cat <<'a292effa503b' > ${AUTARK_HOME}/autark.c #ifndef CONFIG_H #define CONFIG_H #define META_VERSION "0.9.8" #define META_REVISION "b82a078" #define MACRO_MAX_RECURSIVE_CALLS 128 #endif #define _AMALGAMATE_ #define _XOPEN_SOURCE 700 #define _POSIX_C_SOURCE 200809L #define _DEFAULT_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef BASEDEFS_H #define BASEDEFS_H #define QSTR(x__) #x__ #define Q(x__) QSTR(x__) #define AK_LLEN(l__) (sizeof(l__) - 1) #if __GNUC__ >= 4 #define AUR __attribute__((warn_unused_result)) #define AK_ALLOC __attribute__((malloc)) __attribute__((warn_unused_result)) #define AK_NORET __attribute__((noreturn)) #else #define AUR #define AK_ALLOC #define AK_NORET #endif #define AK_CONSTRUCTOR __attribute__((constructor)) #define AK_DESTRUCTOR __attribute__((destructor)) #ifndef O_CLOEXEC #define O_CLOEXEC 0 #endif #define LLEN(l__) (sizeof(l__) - 1) /* Align x_ with v_. v_ must be simple power for 2 value. */ #define ROUNDUP(x_, v_) (((x_) + (v_) - 1) & ~((v_) - 1)) /* Round down align x_ with v_. v_ must be simple power for 2 value. */ #define ROUNDOWN(x_, v_) ((x_) - ((x_) & ((v_) - 1))) #ifdef __GNUC__ #define RCGO(rc__, label__) if (__builtin_expect((!!(rc__)), 0)) goto label__ #else #define RCGO(rc__, label__) if (rc__) goto label__ #endif #define RCHECK(rc__, label__, expr__) \ rc__ = expr__; \ RCGO(rc__, label__) #define RCC(rc__, label__, expr__) RCHECK(rc__, label__, expr__) #define RCN(label__, expr__) \ rc = (expr__); \ if (rc) { \ akerror(rc, 0, 0); \ goto label__; \ } #ifndef MIN #define MIN(a_, b_) ((a_) < (b_) ? (a_) : (b_)) #endif #ifndef MAX #define MAX(a_, b_) ((a_) > (b_) ? (a_) : (b_)) #endif #ifndef _AMALGAMATE_ #include #include #include #include #endif struct value { void *buf; size_t len; int error; }; static inline int value_destroy(struct value *v) { if (v->buf) { free(v->buf); v->buf = 0; } return v->error; } #endif #ifndef LOG_H #define LOG_H #ifndef _AMALGAMATE_ #include "basedefs.h" #include #endif enum akecode { AK_ERROR_OK = 0, AK_ERROR_FAIL = -1, AK_ERROR_INVALID_ARGS = -3, AK_ERROR_ASSERTION = -4, AK_ERROR_OVERFLOW = -5, AK_ERROR_IO = -6, AK_ERROR_SCRIPT_SYNTAX = -10, AK_ERROR_SCRIPT = -11, AK_ERROR_CYCLIC_BUILD_DEPS = -12, AK_ERROR_SCRIPT_ERROR = -13, AK_ERROR_NOT_IMPLEMENTED = -14, AK_ERROR_EXTERNAL_COMMAND = -15, AK_ERROR_DEPENDENCY_UNRESOLVED = -16, AK_ERROR_MACRO_MAX_RECURSIVE_CALLS = -17, }; __attribute__((noreturn)) void akfatal2(const char *msg); __attribute__((noreturn)) void _akfatal(const char *file, int line, int code, const char *fmt, ...); __attribute__((noreturn)) void _akfatal_va(const char *file, int line, int code, const char *fmt, va_list); int _akerror(const char *file, int line, int code, const char *fmt, ...); void _akerror_va(const char *file, int line, int code, const char *fmt, va_list); void _akverbose(const char *file, int line, const char *fmt, ...); void akinfo(const char *fmt, ...); void akwarn(const char *fmt, ...); #define akfatal(code__, fmt__, ...) \ _akfatal(__FILE__, __LINE__, (code__), (fmt__), __VA_ARGS__) #define akfatal_va(code__, fmt__, va__) \ _akfatal_va(__FILE__, __LINE__, (code__), (fmt__), (va__)) #define akerror(code__, fmt__, ...) \ _akerror(__FILE__, __LINE__, (code__), (fmt__), __VA_ARGS__) #define akerror_va(code__, fmt__, va__) \ _akerror_va(__FILE__, __LINE__, (code__), (fmt__), (va__)) #define akverbose(fmt__, ...) \ _akverbose(__FILE__, __LINE__, (fmt__), __VA_ARGS__) #define akassert(exp__) \ do { \ if (!(exp__)) { \ akfatal(AK_ERROR_ASSERTION, Q(exp__), 0); \ } \ } while (0) #define akcheck(exp__) \ do { \ int e = (exp__); \ if (e) akfatal(e, Q(exp__), 0); \ } while (0) #endif #ifndef ALLOC_H #define ALLOC_H #ifndef _AMALGAMATE_ #include "basedefs.h" #include "log.h" #include #include #include #endif AK_ALLOC static inline char* xstrdup(const char *str) { char *ret = strdup(str); if (!ret) { akfatal2("Allocation failed"); } return ret; } AK_ALLOC static inline void* xmalloc(size_t size) { void *ptr = malloc(size); if (!ptr) { akfatal2("Allocation failed"); } return ptr; } AK_ALLOC static inline void* xrealloc(void *p, size_t size) { void *ptr = realloc(p, size); if (!ptr) { akfatal2("Allocation failed"); } return ptr; } AK_ALLOC static inline void* xcalloc(size_t nmemb, size_t size) { void *ptr = calloc(nmemb, size); if (!ptr) { akfatal2("Allocation failed"); } return ptr; } #endif #ifndef POOL_H #define POOL_H #ifndef _AMALGAMATE_ #include #include #endif struct pool_unit { void *heap; struct pool_unit *next; }; struct pool { size_t usiz; /// Used size size_t asiz; /// Allocated size struct pool_unit *unit; /// Current heap unit void *user_data; /// Associated user data char *heap; /// Current pool heap ptr void (*on_pool_destroy)(struct pool*); /// Called when pool destroyed }; struct pool* pool_create_empty(void); struct pool* pool_create_preallocated(size_t); struct pool* pool_create(void (*)(struct pool*)); void pool_destroy(struct pool*); void* pool_alloc(struct pool*, size_t siz); void* pool_calloc(struct pool*, size_t siz); char* pool_strdup(struct pool*, const char*); char* pool_strndup(struct pool*, const char*, size_t len); char* pool_printf_va(struct pool*, const char *format, va_list va); char* pool_printf(struct pool*, const char*, ...) __attribute__((format(__printf__, 2, 3))); const char** pool_split_string( struct pool *pool, const char *haystack, const char *split_chars, int ignore_whitespace); const char** pool_split( struct pool*, const char *split_chars, int ignore_ws, const char *fmt, ...) __attribute__((format(__printf__, 4, 5))); #endif #ifndef XSTR_H #define XSTR_H #ifndef _AMALGAMATE_ #include #include #endif struct xstr { char *ptr; size_t size; size_t asize; }; struct xstr* xstr_create(size_t siz); struct xstr* xstr_create_empty(void); void xstr_cat2(struct xstr*, const void *buf, size_t size); void xstr_cat(struct xstr*, const char *buf); void xstr_shift(struct xstr*, size_t shift_size); void xstr_unshift(struct xstr*, const void *buf, size_t size); void xstr_pop(struct xstr*, size_t pop_size); void xstr_insert(struct xstr*, size_t pos, const void *buf); int xstr_printf_va(struct xstr*, const char *format, va_list va); int xstr_printf(struct xstr*, const char *format, ...) __attribute__((format(__printf__, 2, 3))); struct xstr* xstr_clear(struct xstr*); char* xstr_ptr(struct xstr*); size_t xstr_size(struct xstr*); void xstr_destroy(struct xstr*); char* xstr_destroy_keep_ptr(struct xstr*); void xstr_cat_repeat(struct xstr*, char ch, int count); #endif #ifndef ULIST_H #define ULIST_H #ifndef _AMALGAMATE_ #include "basedefs.h" #endif struct ulist { char *array; unsigned usize; unsigned num; unsigned anum; unsigned start; }; struct ulist* ulist_create(unsigned initial_len, unsigned unit_size); void ulist_init(struct ulist*, unsigned ini_length, unsigned unit_size); void ulist_destroy(struct ulist**); void ulist_destroy_keep(struct ulist*); void ulist_clear(struct ulist*); void ulist_reset(struct ulist*); void* ulist_get(const struct ulist*, unsigned idx); void* ulist_peek(const struct ulist*); void ulist_insert(struct ulist*, unsigned idx, const void *data); void ulist_set(struct ulist*, unsigned idx, const void *data); void ulist_remove(struct ulist*, unsigned idx); void ulist_push(struct ulist*, const void *data); void ulist_pop(struct ulist*); void ulist_unshift(struct ulist*, const void *data); void ulist_shift(struct ulist*); struct ulist* ulist_clone(const struct ulist*); int ulist_find(const struct ulist*, const void *data); int ulist_remove_by(struct ulist*, const void *data); AK_ALLOC char* ulist_to_vlist(const struct ulist *list); #endif #ifndef MAP_H #define MAP_H #ifndef _AMALGAMATE_ #include #endif struct map; struct map_iter; struct map_iter { struct map *hm; const void *key; const void *val; uint32_t bucket; int32_t entry; }; void map_kv_free(void *key, void *val); void map_k_free(void *key, void *val); struct map* map_create( int (*cmp_fn)(const void*, const void*), uint32_t (*hash_fn)(const void*), void (*kv_free_fn)(void*, void*)); void map_destroy(struct map*); struct map* map_create_u64(void (*kv_free_fn)(void*, void*)); struct map* map_create_u32(void (*kv_free_fn)(void*, void*)); struct map* map_create_str(void (*kv_free_fn)(void*, void*)); void map_put(struct map*, void *key, void *val); void map_put_u32(struct map*, uint32_t key, void *val); void map_put_u64(struct map*, uint64_t key, void *val); void map_put_str(struct map*, const char *key, void *val); void map_put_str_no_copy(struct map*, const char *key, void *val); int map_remove(struct map*, const void *key); int map_remove_u64(struct map*, uint64_t key); int map_remove_u32(struct map*, uint32_t key); void* map_get(struct map*, const void *key); void* map_get_u64(struct map*, uint64_t key); void* map_get_u32(struct map*, uint32_t key); uint32_t map_count(const struct map*); void map_clear(struct map*); void map_iter_init(struct map*, struct map_iter*); int map_iter_next(struct map_iter*); #endif #ifndef UTILS_H #define UTILS_H #ifndef _AMALGAMATE_ #include "basedefs.h" #include "xstr.h" #include #include #include #include #endif #define Q_XSTR(s) Q_STR(s) #define Q_STR(s) #s static inline bool utils_char_is_space(char c) { return c == 32 || (c >= 9 && c <= 13); } static inline void utils_chars_replace(char *buf, char c, char r) { for ( ; *buf != '\0'; ++buf) { if (*buf == c) { *buf = r; } } } static inline long utils_lround(double x) { if (x >= 0.0) { x += 0.5; } else { x -= 0.5; } if (x > (double) LONG_MAX) { return LONG_MAX; } else if (x < (double) LONG_MIN) { return LONG_MIN; } return (long) x; } static inline int utils_toupper_ascii(int c) { if (c >= 'a' && c <= 'z') { return c - ('a' - 'A'); // or: c - 32 } return c; } static inline size_t utils_strnlen(const char *s, size_t maxlen) { size_t i; for (i = 0; i < maxlen && s[i]; ++i) ; return i; } static inline char* utils_strncpy(char *dst, const char *src, size_t dst_sz) { if (dst_sz > 1) { size_t len = utils_strnlen(src, dst_sz - 1); memcpy(dst, src, len); dst[len] = '\0'; } else if (dst_sz) { dst[0] = '\0'; } return dst; } static inline char* utils_strnncpy(char *dst, const char *src, size_t src_len, size_t dst_sz) { if (dst_sz > 1 && src_len) { size_t len = MIN(src_len, dst_sz - 1); memcpy(dst, src, len); dst[len] = '\0'; } else if (dst_sz) { dst[0] = '\0'; } return dst; } static inline bool utils_startswith(const char *str, const char *prefix) { if (!str || !prefix) { return false; } size_t str_len = strlen(str); size_t prefix_len = strlen(prefix); if (prefix_len > str_len) { return false; } return strncmp(str, prefix, prefix_len) == 0; } static inline int utils_endswith(const char *str, const char *suffix) { if (!str || !suffix) { return false; } size_t str_len = strlen(str); size_t suffix_len = strlen(suffix); if (suffix_len > str_len) { return false; } if (strcmp(str + str_len - suffix_len, suffix) == 0) { return str_len - suffix_len + 1; } else { return 0; } } long int utils_strtol(const char *v, int base, int *rcp); long long utils_strtoll(const char *v, int base, int *rcp); struct value utils_file_as_buf(const char *path, ssize_t buflen_max); int utils_file_write_buf(const char *path, const char *buf, size_t len, bool append); int utils_copy_file_streams(FILE *src, FILE *dst); int utils_copy_file(const char *src, const char *dst); int utils_copy_dir(const char *src, const char *dst); int utils_copy_dir_to_parent(const char *src, const char *dst); int utils_rename_file(const char *src, const char *dst); void utils_split_values_add(const char *v, struct xstr *xstr); int utils_fd_make_non_blocking(int fd); int64_t utils_current_time_ms(void); const char* utils_json_escape_str(const char *val, ssize_t len, struct xstr *xstr); //----------------------- Vlist struct vlist_iter { const char *item; size_t len; }; static inline bool is_vlist(const char *val) { return (val && *val == '\1'); } static inline void vlist_iter_init(const char *vlist, struct vlist_iter *iter) { iter->item = vlist; iter->len = 0; } static inline bool vlist_iter_next(struct vlist_iter *iter) { iter->item += iter->len; while (*iter->item == '\1') { iter->item++; } if (*iter->item == '\0') { return false; } const char *p = iter->item; while (*p != '\0' && *p != '\1') { ++p; } iter->len = p - iter->item; return true; } #endif #ifndef SPAWN_H #define SPAWN_H #ifndef _AMALGAMATE_ #include #include #include #endif struct spawn; struct spawn* spawn_create(const char *exec, void *user_data); void* spawn_user_data(struct spawn*); pid_t spawn_pid(struct spawn*); int spawn_exit_code(struct spawn*); const char* spawn_arg_add(struct spawn*, const char *arg); bool spawn_arg_exists(struct spawn*, const char *arg); bool spawn_arg_starts_with(struct spawn*, const char *arg); void spawn_env_set(struct spawn*, const char *key, const char *val); void spawn_env_path_prepend(struct spawn*, const char *path); void spawn_set_stdin_provider(struct spawn*, size_t (*provider)(char *buf, size_t buflen, struct spawn*)); void spawn_set_stdout_handler(struct spawn*, void (*handler)(char *buf, size_t buflen, struct spawn*)); void spawn_set_stderr_handler(struct spawn*, void (*handler)(char *buf, size_t buflen, struct spawn*)); void spawn_visit_cmd(struct spawn*, void *user_data, void (*visitor)(int num, const char *arg, void*)); void spawn_set_nowait(struct spawn*, bool nowait); void spawn_set_wstatus(struct spawn*, int wstatus); int spawn_do(struct spawn*); void spawn_destroy(struct spawn*); #endif #ifndef PATHS_H #define PATHS_H #ifndef _AMALGAMATE_ #include "pool.h" #include "basedefs.h" #include #include #endif enum akpath_access { AKPATH_READ = 0x01, AKPATH_WRITE = 0x02, AKPATH_EXEC = 0x04, }; enum akpath_ftype { AKPATH_NOT_EXISTS, AKPATH_TYPE_FILE, AKPATH_TYPE_DIR, AKPATH_LINK, AKPATH_OTHER, }; struct akpath_stat { uint64_t size; uint64_t atime; uint64_t ctime; uint64_t mtime; enum akpath_ftype ftype; }; bool path_is_absolute(const char *path); /// Uses stdlib `realpath` implementation. /// NOTE: Path argument must exists on file system. /// Foo other cases see: path_normalize() const char* path_real(const char *path, char buf[PATH_MAX]); const char* path_real_pool(const char *path, struct pool*); /// Normalize a path buffer to an absolute path without resolving symlinks. /// It operates purely on the path string, and works for non-existing paths. char* path_normalize(const char *path, char buf[PATH_MAX]); char* path_normalize_cwd(const char *path, const char *cwd, char buf[PATH_MAX]); char* path_normalize_pool(const char *path, struct pool*); char* path_normalize_cwd_pool(const char *path, const char *cwd, struct pool*); int path_mkdirs(const char *path); int path_mkdirs_for(const char *path); int path_rm_cache(const char *path); int path_rm_dir_recursive(const char *path); int path_stat(const char *path, struct akpath_stat *stat); int path_stat_fd(int fd, struct akpath_stat *stat); int path_stat_file(FILE *file, struct akpath_stat *stat); uint64_t path_mtime(const char *path); bool path_is_accesible(const char *path, enum akpath_access a); bool path_is_accesible_read(const char *path); bool path_is_accesible_write(const char *path); bool path_is_accesible_exec(const char *path); bool path_is_dir(const char *path); bool path_is_file(const char *path); bool path_is_exist(const char *path); const char* path_is_prefix_for(const char *path, const char *haystack, const char *cwd); AK_ALLOC char* path_relativize(const char *from, const char *to); AK_ALLOC char* path_relativize_cwd(const char *from, const char *to, const char *cwd); // Modifies its argument char* path_dirname(char *path); // Modifies its argument char* path_basename(char *path); AK_ALLOC char* path_join_path_alloc(const char *dir, const char *name, char **out); const char* path_join_path_pool(struct pool *pool, const char *dir, const char *name, const char **out); #endif #ifndef ENV_H #define ENV_H #ifndef _AMALGAMATE_ #include "basedefs.h" // IWYU pragma: keep #include "pool.h" #include "map.h" #include "ulist.h" #include #endif #define TAG_INIT 1 #define TAG_SETUP 2 #define TAG_BUILD 3 #define AUTARK_SCRIPT "Autark" #define AUTARK_CACHE "autark-cache" #define AUTARK_CACHE_OVERLAY_DIR ".overlay" #define AUTARK_FETCHED_REG ".autark-fetched" #define AUTARK_FETCHED_REG_DIST ".autark-fetched-dist" #define AUTARK_FETCH_DEP ".autark-fetch-dep" #define AUTARK_COMPILE_COMMANDS ".compile_commands" #define AUTARK_ROOT_DIR_ENV "AUTARK_ROOT_DIR" // Project root directory #define AUTARK_CACHE_DIR_ENV "AUTARK_CACHE_DIR" // Project cache directory #define AUTARK_CACHE_OVERLAY_DIR_ENV "AUTARK_CACHE_OVERLAY_DIR" // Project cache overlay directory #define AUTARK_UNIT_ENV "AUTARK_UNIT" // Path relative to AUTARK_ROOT_DIR of build process // unit executed #define AUTARK_INSTALL_SRC_DEPS_ENV "AUTARK_INSTALL_SRC_DEPS" // Install src with deps. #define AUTARK_COMPILE_COMMANDS_ENV "AUTARK_COMPILE_COMMANDS" // Path to compile commands file. #define AUTARK_VERBOSE_ENV "AUTARK_VERBOSE" // Autark verbose env key #define UNIT_FLG_ROOT 0x01U // Project root unit #define UNIT_FLG_SRC_CWD 0x02U // Set project source dir as unit CWD #define UNIT_FLG_NO_CWD 0x04U // Do not change CWD for unit #define INSTALL_FLG_SRC_WITH_DEPS 0x01U // Provide distribution with dependencies. #define INSTALL_FLG_SRC_OVERLAYS_APPLIED 0x02U // Source overlays installed struct unit_env_item { const char *val; struct node *n; unsigned tag; }; /// Current execution unit. struct unit { struct map *env; // Environment associated with unit. struct unit_env_item. const char *rel_path; // Path to unit relative to the project root and project cache. const char *basename; // Basename of unit path. const char *dir; // Absolute path to unit dir. const char *source_path; // Absolute unit source path. const char *cache_path; // Absolute path to the unit in cache dir. const char *cache_dir; // Absolute path to the cache directory where unit file is located. struct pool *pool; // Pool used to allocate unit unsigned flags; // Unit flags struct node *n; // Node this unit associated. May be zero. void *impl; // Arbirary implementation data }; /// Unit context in units stack. struct unit_ctx { struct unit *unit; unsigned flags; }; /// Global env struct env { const char *cwd; struct pool *pool; int verbose; int max_parallel_jobs; // Max number of allowed parallel jobs. struct { const char *root_dir; // Project root source dir. Not zero. const char *cache_dir; // Project artifacts cache dir. Not zero. const char *cache_overlay_dir; // Overlay data dir for autark cache. Can be zero. bool cleanup; // Clean project cache before build bool compile_commands; // Generate compile_commands.json database. bool compile_commands_own; // We own compile commands file. bool prepared; // Autark build prepared struct xstr *options; // Ask option values } project; struct { const char *prefix_dir; // Absolute path to install prefix dir. const char *bin_dir; // Path to the bin dir relative to prefix. const char *lib_dir; // Path to lib dir relative to prefix. const char *data_dir; // Path to lib data dir relative to prefix. const char *include_dir; // Path to include headers dir relative to prefix. const char *pkgconf_dir; // Path to pkgconfig dir. const char *man_dir; // Path to man pages dir. unsigned flags; // INSTALL_FLG_XXX bool enabled; // True if install operation should be performed. } install; struct { const char *extra_env_paths; // Extra PATH environment for any program spawn } spawn; struct map *map_path_to_unit; // Path to unit mapping struct ulist stack_units; // Stack of nested unit contexts (struct unit_ctx) struct ulist units; // All created units. (struct unit*) struct { struct xstr *log; } check; }; extern struct env g_env; void on_unit_pool_destroy(struct pool*); struct unit* unit_create(const char *unit_path, unsigned flags, struct pool *pool); struct unit* unit_for_path(const char *path); void unit_push(struct unit*, struct node*); struct unit* unit_pop(void); struct unit* unit_peek(void); struct unit_ctx unit_peek_ctx(void); struct unit* unit_root(void); struct unit* unit_parent(struct node *n); void unit_ch_dir(struct unit_ctx*, char *prevcwd); void unit_ch_cache_dir(struct unit*, char *prevcwd); void unit_ch_src_dir(struct unit*, char *prevcwd); void unit_env_set_val(struct unit*, const char *key, const char *val); void unit_env_set_node(struct unit*, const char *key, struct node *n, unsigned tag); struct node* unit_env_get_node(struct unit *u, const char *key, unsigned *out_tag); const char* unit_env_get(struct node *n, const char *key); const char* unit_env_get_raw(struct unit *u, const char *key); void unit_env_remove(struct unit*, const char *key); const char* env_libdir(void); #endif #ifndef DEPS_H #define DEPS_H #ifndef _AMALGAMATE_ #include "env.h" #include #include #include #include #endif #define DEPS_TYPE_FILE 102 // f #define DEPS_TYPE_FILE_OUTDATED 111 // x #define DEPS_TYPE_NODE_VALUE 118 // v #define DEPS_TYPE_ENV 101 // e #define DEPS_TYPE_SYS_ENV 115 // s #define DEPS_TYPE_ALIAS 97 // a #define DEPS_TYPE_OUTDATED 120 // o #define DEPS_TYPE_FILE_NOT_EXISTS 110 // n #define DEPS_OPEN_TRUNCATE 0x01U #define DEPS_OPEN_READONLY 0x02U #define DEPS_BUF_SZ 262144 struct deps { char type; char flags; int num_registered; /// Number of deps registerd in the current session int64_t serial; const char *resource; const char *alias; FILE *file; char buf[DEPS_BUF_SZ]; }; int deps_open(const char *path, int omode, struct deps *init); bool deps_cur_next(struct deps*); bool deps_cur_is_outdated(struct node *n, struct deps*); int deps_add(struct deps*, char type, char flags, const char *resource, int64_t serial); int deps_add_alias(struct deps*, char flags, const char *resource, const char *alias); int deps_add_env(struct deps *d, char flags, const char *key, const char *value); int deps_add_sys_env(struct deps *d, char flags, const char *key, const char *value); void deps_close(struct deps*); void deps_prune_all(const char *path); #endif #ifndef FETCHREG_H #define FETCHREG_H #ifndef _AMALGAMATE_ #include #endif struct fetchreg; struct fetcherg_entry { const char *url; const char *target; }; int fetchreg_open(const char *path, struct fetchreg **out); bool fetchreg_find(struct fetchreg*, const char *url, void *user_data, void (*cb)(const struct fetcherg_entry*, void*)); int fetchreg_register(struct fetchreg*, const struct fetcherg_entry*); void fetchreg_close(struct fetchreg*); #endif #ifndef NODES_H #define NODES_H #ifndef _AMALGAMATE_ #include "script.h" #endif int node_script_setup(struct node*); int node_meta_setup(struct node*); int node_check_setup(struct node*); int node_set_setup(struct node*); int node_include_setup(struct node*); int node_if_setup(struct node*); int node_subst_setup(struct node*); int node_run_setup(struct node*); int node_echo_setup(struct node*); int node_join_setup(struct node*); int node_cc_setup(struct node*); int node_configure_setup(struct node*); int node_basename_setup(struct node*); int node_foreach_setup(struct node*); int node_in_sources_setup(struct node*); int node_dir_setup(struct node*); int node_option_setup(struct node*); int node_error_setup(struct node*); int node_echo_setup(struct node*); int node_install_setup(struct node*); int node_find_setup(struct node*); int node_macro_setup(struct node*); int node_call_setup(struct node*); int node_fetch_url_setup(struct node*); struct node* call_macro_node(struct node*); struct node* call_first_node(struct node*); void macro_register_call(struct node*); void macro_unregister_call(struct node*); #endif #ifndef AUTARK_H #define AUTARK_H #ifndef _AMALGAMATE_ #include #endif void autark_init(void); void autark_run(int argc, const char **argv); void autark_dispose(void); void autark_build_prepare(const char *script_path); #endif #ifndef SCRIPT_H #define SCRIPT_H #ifndef _AMALGAMATE_ #include "ulist.h" #include "xstr.h" #include "deps.h" #include "map.h" #include #endif // value types #define NODE_TYPE_VALUE 0x01U #define NODE_TYPE_SUBST 0x02U #define NODE_TYPE_SET 0x04U #define NODE_TYPE_JOIN 0x08U #define NODE_TYPE_BASENAME 0x10U #define NODE_TYPE_DIR 0x20U #define NODE_TYPE_FIND 0x40U #define NODE_TYPE_FETCH_URL 0x80U // eof value types #define NODE_TYPE_SCRIPT 0x100U #define NODE_TYPE_BAG 0x200U #define NODE_TYPE_META 0x400U #define NODE_TYPE_CHECK 0x800U #define NODE_TYPE_INCLUDE 0x1000U #define NODE_TYPE_IF 0x2000U #define NODE_TYPE_RUN 0x4000U #define NODE_TYPE_CC 0x8000U #define NODE_TYPE_CONFIGURE 0x10000U #define NODE_TYPE_FOREACH 0x20000U #define NODE_TYPE_IN_SOURCES 0x40000U #define NODE_TYPE_OPTION 0x80000U #define NODE_TYPE_ERROR 0x100000U #define NODE_TYPE_ECHO 0x200000U #define NODE_TYPE_INSTALL 0x400000U #define NODE_TYPE_MACRO 0x800000U #define NODE_TYPE_CALL 0x1000000U #define NODE_TYPE_INSTALL_SOURCES 0x2000000U #define NODE_FLG_BOUND 0x01U #define NODE_FLG_INIT 0x02U #define NODE_FLG_SETUP 0x04U // Vacant: 0x08U #define NODE_FLG_BUILT 0x10U // Node built #define NODE_FLG_POST_BUILT 0x20U // Node post-built #define NODE_FLG_IN_CACHE 0x40U #define NODE_FLG_IN_SRC 0x80U #define NODE_FLG_NO_CWD 0x100U #define NODE_FLG_NEGATE 0x200U #define NODE_FLG_PREFER_SRC_RESOLVING 0x400U // Prefer resolving files in sources #define NODE_FLG_IN_ANY (NODE_FLG_IN_SRC | NODE_FLG_IN_CACHE | NODE_FLG_NO_CWD) #define node_is_init(n__) (((n__)->flags & NODE_FLG_INIT) != 0) #define node_is_setup(n__) (((n__)->flags & NODE_FLG_SETUP) != 0) #define node_is_built(n__) (((n__)->flags & NODE_FLG_BUILT) != 0) #define node_is_post_built(n__) (((n__)->flags & NODE_FLG_POST_BUILT) != 0) #define node_is_value(n__) ((n__)->type == NODE_TYPE_VALUE) #define node_is_can_be_value(n__) ((n__)->type >= NODE_TYPE_VALUE && (n__)->type <= NODE_TYPE_FETCH_URL) #define node_is_rule(n__) !node_is_value(n__) #define NODE_PRINT_INDENT 2 struct node_foreach { char *name; char *value; char *items; unsigned access_cnt; }; struct node { unsigned type; unsigned flags; unsigned flags_owner; /// Extra flaghs set by owner node unsigned index; /// Own index in env::nodes unsigned lnum; /// Node line number const char *name; /// Internal node name const char *vfile; /// Node virtual file const char *value; /// Key or value struct node *child; struct node *next; struct node *parent; // Recursive set struct { struct node *n; bool active; } recur_next; struct sctx *ctx; struct unit *unit; const char* (*value_get)(struct node*); void (*init)(struct node*); void (*setup)(struct node*); void (*build)(struct node*); void (*post_build)(struct node*); void (*dispose)(struct node*); void *impl; }; struct sctx { struct node *root; /// Project root script node (Autark) struct ulist nodes; /// ulist struct map *products; /// Products of nodes (product name -> node) }; int script_open(const char *file, struct sctx **out); int script_include(struct node *parent, const char *file, struct node **out); void script_build(struct sctx*); void script_close(struct sctx**); void script_dump(struct sctx*, struct xstr *out); const char* node_env_get(struct node*, const char *key); void node_env_set(struct node*, const char *key, const char *val); void node_env_set_node(struct node*, const char *key, unsigned tag); struct node* node_by_product(struct node*, const char *prod, char pathbuf[PATH_MAX]); struct node* node_by_product_raw(struct node*, const char *prod); void node_products_add_as_deps(struct node *n, struct deps *deps); void node_product_add(struct node*, const char *prod, char pathbuf[PATH_MAX]); void node_product_add_raw(struct node*, const char *prod); void node_reset(struct node *n); const char* node_value(struct node *n); #define NODE_VISIT_CHILD_SKIP INT_MAX int node_visit(struct node *n, int lvl, void *ctx, int (*visitor)(struct node*, int, void*)); void node_module_setup(struct node *n, unsigned flags); void node_init(struct node *n); void node_setup(struct node *n); void node_build(struct node *n); void node_post_build(struct node *n); struct node* node_find_direct_child(struct node *n, int type, const char *val); struct node* node_find_prev_sibling(struct node *n); struct node* node_find_parent_of_type(struct node *n, int type); struct node_foreach* node_find_parent_foreach(struct node *n); bool node_is_value_may_be_dep_saved(struct node *n, unsigned skip_type); struct node* node_consumes_resolve( struct node *n, struct node *npaths, struct ulist *paths, void (*on_resolved)(const char *path, void *opq), void *opq); void node_add_unit_deps(struct node *n, struct deps*); struct node_resolve { struct node *n; const char *path; void *user_data; void (*on_init)(struct node_resolve*); void (*on_env_value)(struct node_resolve*, const char *key, const char *val); void (*on_resolve)(struct node_resolve*); const char *deps_path_tmp; const char *env_path_tmp; struct ulist resolve_outdated; // struct resolve_outdated struct ulist node_val_deps; // struct node* struct pool *pool; unsigned mode; int num_deps; // Number of dependencies bool force_outdated; }; struct resolve_outdated { char type; char flags; char *path; }; #define NODE_RESOLVE_ENV_ALWAYS 0x01U void node_resolve(struct node_resolve*); __attribute__((noreturn)) void node_fatal(int rc, struct node *n, const char *fmt, ...); void node_info(struct node *n, const char *fmt, ...); void node_warn(struct node *n, const char *fmt, ...); int node_error(int rc, struct node *n, const char *fmt, ...); #define node_check(node__, exp__) \ do { \ int e = (exp__); \ if (e) node_fatal(e, node__, Q(exp__), 0); \ } while (0) struct node* node_clone_and_register(struct node*); int node_bind(struct node*); #endif #ifndef _AMALGAMATE_ #include "xstr.h" #include "alloc.h" #include #include #include #endif struct xstr* xstr_create(size_t siz) { if (!siz) { siz = 16; } struct xstr *xstr; xstr = xmalloc(sizeof(*xstr)); xstr->ptr = xmalloc(siz); xstr->size = 0; xstr->asize = siz; xstr->ptr[0] = '\0'; return xstr; } struct xstr* xstr_create_empty(void) { return xstr_create(0); } void xstr_cat2(struct xstr *xstr, const void *buf, size_t size) { size_t nsize = xstr->size + size + 1; if (xstr->asize < nsize) { while (xstr->asize < nsize) { xstr->asize <<= 1; if (xstr->asize < nsize) { xstr->asize = nsize; } } char *ptr = xrealloc(xstr->ptr, xstr->asize); xstr->ptr = ptr; } memcpy(xstr->ptr + xstr->size, buf, size); xstr->size += size; xstr->ptr[xstr->size] = '\0'; } void xstr_cat(struct xstr *xstr, const char *buf) { size_t size = strlen(buf); xstr_cat2(xstr, buf, size); } void xstr_shift(struct xstr *xstr, size_t shift_size) { if (shift_size == 0) { return; } if (shift_size > xstr->size) { shift_size = xstr->size; } if (xstr->size > shift_size) { memmove(xstr->ptr, xstr->ptr + shift_size, xstr->size - shift_size); } xstr->size -= shift_size; xstr->ptr[xstr->size] = '\0'; } void xstr_unshift(struct xstr *xstr, const void *buf, size_t size) { unsigned nsize = xstr->size + size + 1; if (xstr->asize < nsize) { while (xstr->asize < nsize) { xstr->asize <<= 1; if (xstr->asize < nsize) { xstr->asize = nsize; } } char *ptr = xrealloc(xstr->ptr, xstr->asize); xstr->ptr = ptr; } if (xstr->size) { // shift to right memmove(xstr->ptr + size, xstr->ptr, xstr->size); } memcpy(xstr->ptr, buf, size); xstr->size += size; xstr->ptr[xstr->size] = '\0'; } void xstr_pop(struct xstr *xstr, size_t pop_size) { if (pop_size == 0) { return; } if (pop_size > xstr->size) { pop_size = xstr->size; } xstr->size -= pop_size; xstr->ptr[xstr->size] = '\0'; } void xstr_insert(struct xstr *xstr, size_t pos, const void *buf) { if (pos > xstr->size) { return; } unsigned size = strlen(buf); if (size == 0) { return; } size_t nsize = xstr->size + size + 1; if (xstr->asize < nsize) { while (xstr->asize < nsize) { xstr->asize <<= 1; if (xstr->asize < nsize) { xstr->asize = nsize; } } char *ptr = xrealloc(xstr->ptr, xstr->asize); xstr->ptr = ptr; } memmove(xstr->ptr + pos + size, xstr->ptr + pos, xstr->size - pos + 1 /* \0 */); memcpy(xstr->ptr + pos, buf, size); xstr->size += size; } int xstr_printf_va(struct xstr *xstr, const char *format, va_list va) { int rc = 0; char buf[1024]; va_list cva; va_copy(cva, va); char *wp = buf; int len = vsnprintf(wp, sizeof(buf), format, va); if (len >= sizeof(buf)) { wp = xmalloc(len + 1); len = vsnprintf(wp, len + 1, format, cva); if (len < 0) { rc = EINVAL; goto finish; } } xstr_cat2(xstr, wp, len); finish: va_end(cva); if (wp != buf) { free(wp); } return rc; } int xstr_printf(struct xstr *xstr, const char *format, ...) { va_list ap; va_start(ap, format); int rc = xstr_printf_va(xstr, format, ap); va_end(ap); return rc; } struct xstr* xstr_clear(struct xstr *xstr) { xstr->size = 0; xstr->ptr[0] = '\0'; return xstr; } char* xstr_ptr(struct xstr *xstr) { return xstr->ptr; } size_t xstr_size(struct xstr *xstr) { return xstr->size; } void xstr_destroy(struct xstr *xstr) { if (!xstr) { return; } free(xstr->ptr); free(xstr); } char* xstr_destroy_keep_ptr(struct xstr *xstr) { if (!xstr) { return 0; } char *ptr = xstr->ptr; free(xstr); return ptr; } void xstr_cat_repeat(struct xstr *xstr, char ch, int count) { for (int i = count; i > 0; --i) { char buf[] = { ch }; xstr_cat2(xstr, buf, 1); } } #ifndef _AMALGAMATE_ #include "ulist.h" #include "alloc.h" #include "xstr.h" #include #endif #define _ALLOC_UNIT 32 struct ulist* ulist_create(unsigned initial_len, unsigned unit_size) { struct ulist *list = xmalloc(sizeof(*list)); ulist_init(list, initial_len, unit_size); return list; } void ulist_init(struct ulist *list, unsigned ini_length, unsigned unit_size) { list->usize = unit_size; list->num = 0; list->start = 0; if (!ini_length) { ini_length = _ALLOC_UNIT; } list->anum = ini_length; list->array = xmalloc((size_t) unit_size * ini_length); } void ulist_destroy(struct ulist **lp) { if (lp && *lp) { ulist_destroy_keep(*lp); free(*lp); *lp = 0; } } void ulist_destroy_keep(struct ulist *list) { if (list) { free(list->array); memset(list, 0, sizeof(*list)); } } void ulist_clear(struct ulist *list) { if (list) { free(list->array); ulist_init(list, _ALLOC_UNIT, list->usize); } } void ulist_reset(struct ulist *list) { if (list) { list->num = 0; list->start = 0; } } void* ulist_get(const struct ulist *list, unsigned idx) { if (idx >= list->num) { return 0; } idx += list->start; return list->array + (size_t) idx * list->usize; } void* ulist_peek(const struct ulist *list) { return list->num ? ulist_get(list, list->num - 1) : 0; } void ulist_insert(struct ulist *list, unsigned idx, const void *data) { if (idx > list->num) { return; } idx += list->start; if (list->start + list->num >= list->anum) { size_t anum = list->anum + list->num + 1; void *nptr = xrealloc(list->array, anum * list->usize); list->anum = anum; list->array = nptr; } memmove(list->array + ((size_t) idx + 1) * list->usize, list->array + (size_t) idx * list->usize, (list->start + (size_t) list->num - idx) * list->usize); memcpy(list->array + (size_t) idx * list->usize, data, list->usize); ++list->num; } void ulist_set(struct ulist *list, unsigned idx, const void *data) { if (idx >= list->num) { return; } idx += list->start; memcpy(list->array + (size_t) idx * list->usize, data, list->usize); } void ulist_remove(struct ulist *list, unsigned idx) { if (idx >= list->num) { return; } idx += list->start; --list->num; memmove(list->array + (size_t) idx * list->usize, list->array + ((size_t) idx + 1) * list->usize, (list->start + (size_t) list->num - idx) * list->usize); if ((list->anum > _ALLOC_UNIT) && (list->anum >= list->num * 2)) { if (list->start) { memmove(list->array, list->array + (size_t) list->start * list->usize, (size_t) list->num * list->usize); list->start = 0; } size_t anum = list->num > _ALLOC_UNIT ? list->num : _ALLOC_UNIT; void *nptr = xrealloc(list->array, anum * list->usize); list->anum = anum; list->array = nptr; } } void ulist_push(struct ulist *list, const void *data) { size_t idx = list->start + list->num; if (idx >= list->anum) { size_t anum = list->anum + list->num + 1; void *nptr = xrealloc(list->array, anum * list->usize); list->anum = anum; list->array = nptr; } memcpy(list->array + idx * list->usize, data, list->usize); ++list->num; } void ulist_pop(struct ulist *list) { if (!list->num) { return; } size_t num = list->num - 1; if ((list->anum > _ALLOC_UNIT) && (list->anum >= num * 2)) { if (list->start) { memmove(list->array, list->array + (size_t) list->start * list->usize, num * list->usize); list->start = 0; } size_t anum = num > _ALLOC_UNIT ? num : _ALLOC_UNIT; void *nptr = xrealloc(list->array, anum * list->usize); list->anum = anum; list->array = nptr; } list->num = num; } void ulist_unshift(struct ulist *list, const void *data) { if (!list->start) { if (list->num >= list->anum) { size_t anum = list->anum + list->num + 1; void *nptr = xrealloc(list->array, anum * list->usize); list->anum = anum; list->array = nptr; } list->start = list->anum - list->num; memmove(list->array + (size_t) list->start * list->usize, list->array, (size_t) list->num * list->usize); } memcpy(list->array + ((size_t) list->start - 1) * list->usize, data, list->usize); --list->start; ++list->num; } void ulist_shift(struct ulist *list) { if (!list->num) { return; } size_t num = list->num - 1; size_t start = list->start + 1; if ((list->anum > _ALLOC_UNIT) && (list->anum >= num * 2)) { if (start) { memmove(list->array, list->array + start * list->usize, num * list->usize); start = 0; } size_t anum = num > _ALLOC_UNIT ? num : _ALLOC_UNIT; void *nptr = xrealloc(list->array, anum * list->usize); list->anum = anum; list->array = nptr; } list->start = start; list->num = num; } struct ulist* ulist_clone(const struct ulist *list) { if (!list->num) { return ulist_create(list->anum, list->usize); } struct ulist *nlist = xmalloc(sizeof(*nlist)); unsigned anum = list->num > _ALLOC_UNIT ? list->num : _ALLOC_UNIT; nlist->array = xmalloc(anum * list->usize); memcpy(nlist->array, list->array + list->start, list->num * list->usize); nlist->usize = list->usize; nlist->num = list->num; nlist->anum = anum; nlist->start = 0; return nlist; } int ulist_find(const struct ulist *list, const void *data) { for (unsigned i = list->start; i < list->start + list->num; ++i) { void *ptr = list->array + i * list->usize; if (memcmp(data, ptr, list->usize) == 0) { return i - list->start; } } return -1; } int ulist_remove_by(struct ulist *list, const void *data) { for (unsigned i = list->start; i < list->start + list->num; ++i) { void *ptr = list->array + i * list->usize; if (memcmp(data, ptr, list->usize) == 0) { ulist_remove(list, i - list->start); return i; } } return -1; } char* ulist_to_vlist(const struct ulist *list) { akassert(list && list->usize == sizeof(char*)); struct xstr *xstr = xstr_create_empty(); for (int i = 0; i < list->num; ++i) { const char *c = *(const char**) ulist_get(list, i); xstr_cat(xstr, "\1"); xstr_cat(xstr, c); } return xstr_destroy_keep_ptr(xstr); } #ifndef _AMALGAMATE_ #include "basedefs.h" #include "pool.h" #include "utils.h" #include "alloc.h" #include #include #endif #define _UNIT_ALIGN_SIZE 8UL static void _extend(struct pool *pool, size_t siz); struct pool* pool_create_empty(void) { return xcalloc(1, sizeof(struct pool)); } struct pool* pool_create_preallocated(size_t sz) { struct pool *pool = pool_create_empty(); _extend(pool, sz); return pool; } struct pool* pool_create(void (*on_pool_destroy)(struct pool*)) { struct pool *pool = pool_create_empty(); pool->on_pool_destroy = on_pool_destroy; return pool; } void pool_destroy(struct pool *pool) { if (!pool) { return; } if (pool->on_pool_destroy) { pool->on_pool_destroy(pool); } for (struct pool_unit *u = pool->unit, *next; u; u = next) { next = u->next; free(u->heap); free(u); } free(pool); } static void _extend(struct pool *pool, size_t siz) { struct pool_unit *nunit = xmalloc(sizeof(*nunit)); siz = ROUNDUP(siz, _UNIT_ALIGN_SIZE); nunit->heap = xmalloc(siz); nunit->next = pool->unit; pool->heap = nunit->heap; pool->unit = nunit; pool->usiz = 0; pool->asiz = siz; } void* pool_alloc(struct pool *pool, size_t siz) { siz = ROUNDUP(siz, _UNIT_ALIGN_SIZE); size_t usiz = pool->usiz + siz; void *h = pool->heap; if (usiz > pool->asiz) { usiz = usiz + pool->asiz; _extend(pool, usiz); h = pool->heap; } pool->usiz += siz; pool->heap += siz; return h; } void* pool_calloc(struct pool *pool, size_t siz) { void *p = pool_alloc(pool, siz); memset(p, 0, siz); return p; } char* pool_strdup(struct pool *pool, const char *str) { if (str) { size_t len = strlen(str); char *ret = pool_alloc(pool, len + 1); memcpy(ret, str, len); ret[len] = '\0'; return ret; } else { return 0; } } char* pool_strndup(struct pool *pool, const char *str, size_t len) { if (str) { len = utils_strnlen(str, len); char *ret = pool_alloc(pool, len + 1); memcpy(ret, str, len); ret[len] = '\0'; return ret; } else { return 0; } } static inline int _printf_estimate_size(const char *format, va_list ap) { char buf[1]; return vsnprintf(buf, sizeof(buf), format, ap) + 1; } static char* _printf_va(struct pool *pool, unsigned size, const char *format, va_list ap) { char *wbuf = pool_alloc(pool, size); if (!wbuf) { return 0; } vsnprintf(wbuf, size, format, ap); return wbuf; } char* pool_printf_va(struct pool *pool, const char *format, va_list va) { va_list cva; va_copy(cva, va); int size = _printf_estimate_size(format, va); va_end(va); char *res = _printf_va(pool, size, format, cva); va_end(cva); return res; } char* pool_printf(struct pool *pool, const char *fmt, ...) { va_list ap; va_start(ap, fmt); int size = _printf_estimate_size(fmt, ap); va_end(ap); va_start(ap, fmt); char *res = _printf_va(pool, size, fmt, ap); va_end(ap); return res; } const char** pool_split_string( struct pool *pool, const char *haystack, const char *split_chars, int ignore_whitespace) { size_t hsz = strlen(haystack); const char **ret = (const char**) pool_alloc(pool, (hsz + 1) * sizeof(char*)); const char *sp = haystack; const char *ep = sp; int j = 0; for (int i = 0; *ep; ++i, ++ep) { const char ch = haystack[i]; const char *sch = strchr(split_chars, ch); if ((ep >= sp) && (sch || (*(ep + 1) == '\0'))) { if (!sch && (*(ep + 1) == '\0')) { ++ep; } if (ignore_whitespace) { while (utils_char_is_space(*sp)) ++sp; while (utils_char_is_space(*(ep - 1))) --ep; } if (ep >= sp) { char *s = pool_alloc(pool, ep - sp + 1); memcpy(s, sp, ep - sp); s[ep - sp] = '\0'; ret[j++] = s; ep = haystack + i; } sp = haystack + i + 1; } } ret[j] = 0; return ret; } #ifndef _AMALGAMATE_ #include "basedefs.h" #include "log.h" #include "xstr.h" #include "alloc.h" #include "utils.h" #include "config.h" #include #include #include #include #include #endif #define LEVEL_VERBOSE 0 #define LEVEL_INFO 1 #define LEVEL_WARN 2 #define LEVEL_ERROR 3 #define LEVEL_FATAL 4 static const char* _error_get(int code) { switch (code) { case AK_ERROR_OVERFLOW: return "Value/argument overflow. (AK_ERROR_OVERFLOW)"; case AK_ERROR_ASSERTION: return "Assertion failed (AK_ERROR_ASSERTION)"; case AK_ERROR_INVALID_ARGS: return "Invalid function arguments (AKL_ERROR_INVALID_ARGS)"; case AK_ERROR_FAIL: return "Fail (AK_ERROR_FAIL)"; case AK_ERROR_IO: return "IO Error (AK_ERROR_IO)"; case AK_ERROR_SCRIPT_SYNTAX: return "Invalid autark config syntax (AK_ERROR_SCRIPT_SYNTAX)"; case AK_ERROR_SCRIPT: return "Autark script error (AK_ERROR_SCRIPT)"; case AK_ERROR_CYCLIC_BUILD_DEPS: return "Detected cyclic build dependency (AK_ERROR_CYCLIC_BUILD_DEPS)"; case AK_ERROR_SCRIPT_ERROR: return "Build script error (AK_ERROR_SCRIPT_ERROR)"; case AK_ERROR_NOT_IMPLEMENTED: return "Not implemented (AK_ERROR_NOT_IMPLEMENTED)"; case AK_ERROR_EXTERNAL_COMMAND: return "External command failed (AK_ERROR_EXTERNAL_COMMAND)"; case AK_ERROR_DEPENDENCY_UNRESOLVED: return "I don't know how to build dependency (AK_ERROR_DEPENDENCY_UNRESOLVED)"; case AK_ERROR_MACRO_MAX_RECURSIVE_CALLS: return "Max number of recursive macro calls reached: " Q(MACRO_MAX_RECURSIVE_CALLS) " (AK_ERROR_MACRO_MAX_RECURSIVE_CALLS)"; case AK_ERROR_OK: return "OK"; default: return strerror(code); } } static char* _log_basename(char *path) { size_t i; if (!path || *path == '\0') { return "."; } i = strlen(path) - 1; for ( ; i && path[i] == '/'; i--) path[i] = 0; for ( ; i && path[i - 1] != '/'; i--) ; return path + i; } static void _event_va( int level, const char *file, int line, int code, const char *format, va_list va) { struct xstr *xstr = 0; xstr = xstr_create_empty(); switch (level) { case LEVEL_FATAL: xstr_cat2(xstr, "FATAL ", LLEN("FATAL ")); break; case LEVEL_INFO: xstr_cat2(xstr, "INFO ", LLEN("INFO ")); break; case LEVEL_ERROR: xstr_cat2(xstr, "ERROR ", LLEN("ERROR ")); break; case LEVEL_WARN: xstr_cat2(xstr, "WARN ", LLEN("WARN ")); break; case LEVEL_VERBOSE: xstr_cat2(xstr, "VERBOSE ", LLEN("VERBOSE ")); break; default: xstr_cat2(xstr, "XXX ", LLEN("XXX ")); break; } if (file) { char *cfile = xstrdup(file); if (cfile) { char *p = _log_basename(cfile); xstr_printf(xstr, "%s:%d ", p, line); free(cfile); } } if (code) { const char *err = _error_get(code); xstr_printf(xstr, "%d|", code); if (err) { xstr_cat(xstr, err); } } if (format) { if (code) { xstr_cat2(xstr, "|", 1); } xstr_printf_va(xstr, format, va); } xstr_cat2(xstr, "\n", 1); utils_chars_replace(xstr_ptr(xstr), '\1', '^'); fputs(xstr_ptr(xstr), stderr); xstr_destroy(xstr); } void akfatal2(const char *msg) { if (msg) { fputs(msg, stderr); fputc('\n', stderr); } _exit(254); } void _akfatal(const char *file, int line, int code, const char *fmt, ...) { va_list ap; va_start(ap, fmt); _event_va(LEVEL_FATAL, file, line, code, fmt, ap); va_end(ap); akfatal2(0); } int _akerror(const char *file, int line, int code, const char *fmt, ...) { va_list ap; va_start(ap, fmt); _event_va(LEVEL_ERROR, file, line, code, fmt, ap); va_end(ap); return code; } void _akverbose(const char *file, int line, const char *fmt, ...) { va_list ap; va_start(ap, fmt); _event_va(LEVEL_VERBOSE, file, line, 0, fmt, ap); va_end(ap); } void akinfo(const char *fmt, ...) { va_list ap; va_start(ap, fmt); _event_va(LEVEL_INFO, 0, 0, 0, fmt, ap); va_end(ap); } void akwarn(const char *fmt, ...) { va_list ap; va_start(ap, fmt); _event_va(LEVEL_WARN, 0, 0, 0, fmt, ap); va_end(ap); } void _akerror_va(const char *file, int line, int code, const char *fmt, va_list ap) { _event_va(LEVEL_ERROR, file, line, code, fmt, ap); } void _akfatal_va(const char *file, int line, int code, const char *fmt, va_list ap) { _event_va(LEVEL_FATAL, file, line, code, fmt, ap); akfatal2(0); } #ifndef _AMALGAMATE_ #include "map.h" #include "log.h" #include "alloc.h" #include #include #include #include #endif #define MIN_BUCKETS 64 #define STEPS 4 struct _map_entry { void *key; void *val; uint32_t hash; }; struct _map_bucket { struct _map_entry *entries; uint32_t used; uint32_t total; }; struct map { uint32_t count; uint32_t buckets_mask; struct _map_bucket *buckets; int (*cmp_fn)(const void*, const void*); uint32_t (*hash_key_fn)(const void*); void (*kv_free_fn)(void*, void*); int int_key_as_pointer_value; }; static void _map_noop_kv_free(void *key, void *val) { } static void _map_noop_uint64_kv_free(void *key, void *val) { if (key) { free(key); } } static inline uint32_t _map_n_buckets(struct map *hm) { return hm->buckets_mask + 1; } static int _map_ptr_cmp(const void *v1, const void *v2) { return v1 > v2 ? 1 : v1 < v2 ? -1 : 0; } static int _map_uint32_cmp(const void *v1, const void *v2) { intptr_t p1 = (intptr_t) v1; intptr_t p2 = (intptr_t) v2; return p1 > p2 ? 1 : p1 < p2 ? -1 : 0; } static int _map_uint64_cmp(const void *v1, const void *v2) { if (sizeof(uintptr_t) >= sizeof(uint64_t)) { intptr_t p1 = (intptr_t) v1; intptr_t p2 = (intptr_t) v2; return p1 > p2 ? 1 : p1 < p2 ? -1 : 0; } else { uint64_t l1, l2; memcpy(&l1, v1, sizeof(l1)); memcpy(&l2, v2, sizeof(l2)); return l1 > l2 ? 1 : l1 < l2 ? -1 : 0; } } static inline uint32_t _map_hash_uint32(uint32_t x) { x ^= x >> 16; x *= 0x85ebca6bU; x ^= x >> 13; x *= 0xc2b2ae35U; x ^= x >> 16; return x; } static inline uint32_t _map_hash_uint64(uint64_t x) { x ^= x >> 33; x *= 0xff51afd7ed558ccdULL; x ^= x >> 33; x *= 0xc4ceb9fe1a85ec53ULL; x ^= x >> 33; return x; } static inline uint32_t _map_hash_uint64_key(const void *key) { uint64_t lv = 0; memcpy(&lv, key, sizeof(void*)); return _map_hash_uint64(lv); } static inline uint32_t _map_hash_uint32_key(const void *key) { return _map_hash_uint32((uintptr_t) key); } static inline uint32_t _map_hash_buf_key(const void *key) { const char *str = key; uint32_t hash = 2166136261U; while (*str) { hash ^= (uint8_t) (*str); hash *= 16777619U; str++; } return hash; } static struct _map_entry* _map_entry_add(struct map *hm, void *key, uint32_t hash) { struct _map_entry *entry; struct _map_bucket *bucket = hm->buckets + (hash & hm->buckets_mask); if (bucket->used + 1 >= bucket->total) { if (UINT32_MAX - bucket->total < STEPS) { akfatal(AK_ERROR_OVERFLOW, 0, 0, 0); } uint32_t new_total = bucket->total + STEPS; struct _map_entry *new_entries = xrealloc(bucket->entries, new_total * sizeof(new_entries[0])); bucket->entries = new_entries; bucket->total = new_total; } entry = bucket->entries; for (struct _map_entry *end = entry + bucket->used; entry < end; ++entry) { if ((hash == entry->hash) && (hm->cmp_fn(key, entry->key) == 0)) { return entry; } } ++bucket->used; ++hm->count; entry->hash = hash; entry->key = 0; entry->val = 0; return entry; } static struct _map_entry* _map_entry_find(struct map *hm, const void *key, uint32_t hash) { struct _map_bucket *bucket = hm->buckets + (hash & hm->buckets_mask); struct _map_entry *entry = bucket->entries; for (struct _map_entry *end = entry + bucket->used; entry < end; ++entry) { if (hash == entry->hash && hm->cmp_fn(key, entry->key) == 0) { return entry; } } return 0; } static void _map_rehash(struct map *hm, uint32_t num_buckets) { struct _map_bucket *buckets = xcalloc(num_buckets, sizeof(*buckets)); struct _map_bucket *bucket, *bucket_end = hm->buckets + _map_n_buckets(hm); struct map hm_copy = *hm; hm_copy.count = 0; hm_copy.buckets_mask = num_buckets - 1; hm_copy.buckets = buckets; for (bucket = hm->buckets; bucket < bucket_end; ++bucket) { struct _map_entry *entry_old = bucket->entries; if (entry_old) { struct _map_entry *entry_old_end = entry_old + bucket->used; for ( ; entry_old < entry_old_end; ++entry_old) { struct _map_entry *entry_new = _map_entry_add(&hm_copy, entry_old->key, entry_old->hash); entry_new->key = entry_old->key; entry_new->val = entry_old->val; } } } for (bucket = hm->buckets; bucket < bucket_end; ++bucket) { free(bucket->entries); } free(hm->buckets); hm->buckets = buckets; hm->buckets_mask = num_buckets - 1; return; } static void _map_entry_remove(struct map *hm, struct _map_bucket *bucket, struct _map_entry *entry) { hm->kv_free_fn(hm->int_key_as_pointer_value ? 0 : entry->key, entry->val); if (bucket->used > 1) { struct _map_entry *entry_last = bucket->entries + bucket->used - 1; if (entry != entry_last) { memcpy(entry, entry_last, sizeof(*entry)); } } --bucket->used; --hm->count; if ((hm->buckets_mask > MIN_BUCKETS - 1) && (hm->count < hm->buckets_mask / 2)) { _map_rehash(hm, _map_n_buckets(hm) / 2); } else { uint32_t steps_used = bucket->used / STEPS; uint32_t steps_total = bucket->total / STEPS; if (steps_used + 1 < steps_total) { struct _map_entry *entries_new = realloc(bucket->entries, ((size_t) steps_used + 1) * STEPS * sizeof(entries_new[0])); if (entries_new) { bucket->entries = entries_new; bucket->total = (steps_used + 1) * STEPS; } } } } void map_kv_free(void *key, void *val) { free(key); free(val); } void map_k_free(void *key, void *val) { free(key); } struct map* map_create( int (*cmp_fn)(const void*, const void*), uint32_t (*hash_key_fn)(const void*), void (*kv_free_fn)(void*, void*)) { if (!hash_key_fn) { return 0; } if (!cmp_fn) { cmp_fn = _map_ptr_cmp; } if (!kv_free_fn) { kv_free_fn = _map_noop_kv_free; } struct map *hm = xmalloc(sizeof(*hm)); hm->buckets = xcalloc(MIN_BUCKETS, sizeof(hm->buckets[0])); hm->cmp_fn = cmp_fn; hm->hash_key_fn = hash_key_fn; hm->kv_free_fn = kv_free_fn; hm->buckets_mask = MIN_BUCKETS - 1; hm->count = 0; hm->int_key_as_pointer_value = 0; return hm; } void map_destroy(struct map *hm) { if (hm) { for (struct _map_bucket *b = hm->buckets, *be = hm->buckets + _map_n_buckets(hm); b < be; ++b) { if (b->entries) { for (struct _map_entry *e = b->entries, *ee = b->entries + b->used; e < ee; ++e) { hm->kv_free_fn(hm->int_key_as_pointer_value ? 0 : e->key, e->val); } free(b->entries); } } free(hm->buckets); free(hm); } } struct map* map_create_u64(void (*kv_free_fn)(void*, void*)) { if (!kv_free_fn) { kv_free_fn = _map_noop_uint64_kv_free; } struct map *hm = map_create(_map_uint64_cmp, _map_hash_uint64_key, kv_free_fn); if (hm) { if (sizeof(uintptr_t) >= sizeof(uint64_t)) { hm->int_key_as_pointer_value = 1; } } return hm; } struct map* map_create_u32(void (*kv_free_fn)(void*, void*)) { struct map *hm = map_create(_map_uint32_cmp, _map_hash_uint32_key, kv_free_fn); if (hm) { hm->int_key_as_pointer_value = 1; } return hm; } struct map* map_create_str(void (*kv_free_fn)(void*, void*)) { return map_create((int (*)(const void*, const void*)) strcmp, _map_hash_buf_key, kv_free_fn); } void map_put(struct map *hm, void *key, void *val) { uint32_t hash = hm->hash_key_fn(key); struct _map_entry *entry = _map_entry_add(hm, key, hash); hm->kv_free_fn(hm->int_key_as_pointer_value ? 0 : entry->key, entry->val); entry->key = key; entry->val = val; if (hm->count > hm->buckets_mask) { _map_rehash(hm, _map_n_buckets(hm) * 2); } } void map_put_u32(struct map *hm, uint32_t key, void *val) { map_put(hm, (void*) (uintptr_t) key, val); } void map_put_u64(struct map *hm, uint64_t key, void *val) { if (hm->int_key_as_pointer_value) { map_put(hm, (void*) (uintptr_t) key, val); } else { uint64_t *kv = xmalloc(sizeof(*kv)); memcpy(kv, &key, sizeof(*kv)); map_put(hm, kv, val); } } void map_put_str(struct map *hm, const char *key_, void *val) { char *key = xstrdup(key_); map_put(hm, key, val); } void map_put_str_no_copy(struct map *hm, const char *key, void *val) { map_put(hm, (void*) key, val); } int map_remove(struct map *hm, const void *key) { uint32_t hash = hm->hash_key_fn(key); struct _map_bucket *bucket = hm->buckets + (hash & hm->buckets_mask); struct _map_entry *entry = _map_entry_find(hm, key, hash); if (entry) { _map_entry_remove(hm, bucket, entry); return 1; } else { return 0; } } int map_remove_u64(struct map *hm, uint64_t key) { if (hm->int_key_as_pointer_value) { return map_remove(hm, (void*) (uintptr_t) key); } else { return map_remove(hm, &key); } } int map_remove_u32(struct map *hm, uint32_t key) { return map_remove(hm, (void*) (uintptr_t) key); } void* map_get(struct map *hm, const void *key) { uint32_t hash = hm->hash_key_fn(key); struct _map_entry *entry = _map_entry_find(hm, key, hash); if (entry) { return entry->val; } else { return 0; } } void* map_get_u64(struct map *hm, uint64_t key) { if (hm->int_key_as_pointer_value) { return map_get(hm, (void*) (intptr_t) key); } else { return map_get(hm, &key); } } void* map_get_u32(struct map *hm, uint32_t key) { return map_get(hm, (void*) (intptr_t) key); } uint32_t map_count(const struct map *hm) { return hm->count; } void map_clear(struct map *hm) { if (!hm) { return; } for (struct _map_bucket *b = hm->buckets, *be = hm->buckets + _map_n_buckets(hm); b < be; ++b) { for (struct _map_entry *e = b->entries, *ee = b->entries + b->used; e < ee; ++e) { hm->kv_free_fn(hm->int_key_as_pointer_value ? 0 : e->key, e->val); } free(b->entries); b->used = 0; b->total = 0; b->entries = 0; } if (_map_n_buckets(hm) > MIN_BUCKETS) { struct _map_bucket *buckets_new = realloc(hm->buckets, sizeof(buckets_new[0]) * MIN_BUCKETS); if (buckets_new) { memset(buckets_new, 0, sizeof(buckets_new[0]) * MIN_BUCKETS); hm->buckets = buckets_new; hm->buckets_mask = MIN_BUCKETS - 1; } } hm->count = 0; } void map_iter_init(struct map *hm, struct map_iter *iter) { iter->hm = hm; iter->entry = -1; iter->bucket = 0; iter->key = 0; iter->val = 0; } int map_iter_next(struct map_iter *iter) { if (!iter->hm) { return 0; } struct _map_entry *entry; struct _map_bucket *bucket = iter->hm->buckets + iter->bucket; ++iter->entry; if ((uint32_t) iter->entry >= bucket->used) { uint32_t n = _map_n_buckets(iter->hm); iter->entry = 0; for (++iter->bucket; iter->bucket < n; ++iter->bucket) { bucket = iter->hm->buckets + iter->bucket; if (bucket->used > 0) { break; } } if (iter->bucket >= n) { return 0; } } entry = bucket->entries + iter->entry; iter->key = entry->key; iter->val = entry->val; return 1; } #ifndef _AMALGAMATE_ #include "utils.h" #include "paths.h" #include "xstr.h" #include "log.h" #include #include #include #include #include #include #include #include #endif struct value utils_file_as_buf(const char *path, ssize_t buflen_max) { struct value ret = { 0 }; struct xstr *xstr = xstr_create_empty(); char buf[8192]; int fd = open(path, O_RDONLY | O_CLOEXEC); if (fd == -1) { ret.error = errno; xstr_destroy(xstr); return ret; } while (buflen_max != 0 && ret.error == 0) { ssize_t rb = read(fd, buf, sizeof(buf)); if (rb > 0) { if (buflen_max > -1) { if (rb > buflen_max) { rb = buflen_max; ret.error = AK_ERROR_OVERFLOW; } buflen_max -= rb; } xstr_cat2(xstr, buf, rb); } else if (rb == -1) { if (errno != EINTR && errno != EAGAIN) { ret.error = errno; break; } } else { break; } } ret.len = xstr_size(xstr); ret.buf = xstr_destroy_keep_ptr(xstr); return ret; } int utils_file_write_buf(const char *path, const char *buf, size_t len, bool append) { int flags = O_WRONLY | O_CREAT; if (append) { flags |= O_APPEND; } else { flags |= O_TRUNC; } int fd = open(path, flags, 0644); if (fd == -1) { return errno; } for (ssize_t w, tow = len; tow > 0; ) { w = write(fd, buf + len - tow, tow); if (w >= 0) { tow -= w; } else if (w < 0) { if (errno == EAGAIN) { continue; } int ret = errno; close(fd); return ret; } } close(fd); return 0; } int utils_copy_file_streams(FILE *sf, FILE *df) { char buf[8192]; size_t nr = 0; while (1) { nr = fread(buf, 1, sizeof(buf), sf); if (nr) { size_t offset = 0; while (offset < nr) { size_t nw = fwrite(buf + offset, 1, nr - offset, df); if (!nw) { return AK_ERROR_IO; } offset += nw; } } else if (feof(sf)) { break; } else if (ferror(sf)) { return AK_ERROR_IO; } } return 0; } int utils_copy_file(const char *src, const char *dst) { int rc = 0; char buf[8192]; FILE *sf = fopen(src, "rb"); if (!sf) { return errno; } FILE *df = fopen(dst, "wb"); if (!df) { rc = errno; fclose(sf); return rc; } size_t nr = 0; while (1) { nr = fread(buf, 1, sizeof(buf), sf); if (nr) { size_t offset = 0; while (offset < nr) { size_t nw = fwrite(buf + offset, 1, nr - offset, df); if (!nw) { rc = AK_ERROR_IO; goto finish; } offset += nw; } } else if (feof(sf)) { break; } else if (ferror(sf)) { rc = AK_ERROR_IO; break; } } finish: fclose(sf); if (fclose(df)) { if (!rc) { rc = AK_ERROR_IO; } } return rc; } int utils_rename_file(const char *src, const char *dst) { if (rename(src, dst) == -1) { if (errno == EXDEV) { int rc = utils_copy_file(src, dst); if (!rc) { unlink(src); } return rc; } else { return errno; } } return 0; } static inline int _utils_same_file(const struct stat *a, const struct stat *b) { return a->st_dev == b->st_dev && a->st_ino == b->st_ino; } static int _utils_path_is_same_or_child(const char *parent, const char *path) { size_t len = strlen(parent); if (strncmp(parent, path, len) != 0) { return 0; } if (path[len] == '\0') { return 1; } if (len == 1 && parent[0] == '/') { return path[0] == '/'; } return path[len] == '/'; } static char* _utils_real_existing_ancestor(const char *path) { char *current = strdup(path); if (!current) { errno = ENOMEM; return 0; } for ( ; ; ) { char *real = realpath(current, 0); if (real) { free(current); return real; } if (errno != ENOENT && errno != ENOTDIR) { int rc = errno; free(current); errno = rc; return 0; } size_t len = strlen(current); while (len > 1 && current[len - 1] == '/') { current[--len] = '\0'; } char *slash = strrchr(current, '/'); if (!slash) { free(current); current = strdup("."); if (!current) { errno = ENOMEM; return 0; } } else if (slash == current) { current[1] = '\0'; } else { *slash = '\0'; } } } static int _utils_check_overlap(const char *src, const char *dst) { struct stat st; if (lstat(src, &st) != 0) { return errno; } if (S_ISLNK(st.st_mode)) { return ELOOP; } if (!S_ISDIR(st.st_mode)) { return ENOTDIR; } char *src_real = realpath(src, 0); if (!src_real) { return errno; } char *dst_ancestor = _utils_real_existing_ancestor(dst); if (!dst_ancestor) { int rc = errno; free(src_real); return rc; } int rc = _utils_path_is_same_or_child(src_real, dst_ancestor) ? EINVAL : 0; if (!rc) { if (lstat(dst, &st) == 0) { if (S_ISLNK(st.st_mode)) { rc = ELOOP; } else if (!S_ISDIR(st.st_mode)) { rc = ENOTDIR; } else { char *dst_real = realpath(dst, 0); if (!dst_real) { rc = errno; } else { if (_utils_path_is_same_or_child( dst_real, src_real)) { rc = EINVAL; } free(dst_real); } } } else if (errno != ENOENT) { rc = errno; } } free(dst_ancestor); free(src_real); return rc; } static int _utils_copy_symlink(const char *src, const char *dst, const struct stat *src_st) { ssize_t len; char *target = 0; size_t capacity = src_st->st_size > 0 ? (size_t) src_st->st_size + 1 : 256; for ( ; ; ) { target = malloc(capacity + 1); if (!target) { return ENOMEM; } len = readlink(src, target, capacity); if (len < 0) { int rc = errno; free(target); return rc; } if ((size_t) len < capacity) { break; } free(target); if (capacity > SIZE_MAX / 2) { return EOVERFLOW; } capacity *= 2; } target[len] = '\0'; struct stat dst_st; if (lstat(dst, &dst_st) == 0) { if (S_ISDIR(dst_st.st_mode)) { free(target); return EISDIR; } if (unlink(dst) != 0) { int rc = errno; free(target); return rc; } } else if (errno != ENOENT) { int rc = errno; free(target); return rc; } int rc = symlink(target, dst) == 0 ? 0 : errno; free(target); return rc; } static int _utils_copy_regular( const char *src, const char *dst, const struct stat *src_st) { struct stat dst_st; if (lstat(dst, &dst_st) == 0) { if (S_ISDIR(dst_st.st_mode)) { return EISDIR; } if (S_ISLNK(dst_st.st_mode)) { if (unlink(dst) != 0) { return errno; } } else if (!S_ISREG(dst_st.st_mode)) { return ENOTSUP; } else if (_utils_same_file(src_st, &dst_st)) { return EINVAL; } } else if (errno != ENOENT) { return errno; } int rc = utils_copy_file(src, dst); if ( !rc && chmod(dst, src_st->st_mode & 07777) != 0) { rc = errno; } return rc; } static int _utils_copy_dir_recursive(const char *src, const char *dst) { struct stat src_st; struct stat dst_st; if (lstat(src, &src_st) != 0) { return errno; } if (S_ISLNK(src_st.st_mode)) { return ELOOP; } if (!S_ISDIR(src_st.st_mode)) { return ENOTDIR; } int created = 0; if (lstat(dst, &dst_st) == 0) { if (S_ISLNK(dst_st.st_mode)) { return ELOOP; } if (!S_ISDIR(dst_st.st_mode)) { return ENOTDIR; } if (_utils_same_file(&src_st, &dst_st)) { return EINVAL; } } else if (errno == ENOENT) { if (mkdir(dst, 0700) != 0) { return errno; } created = 1; } else { return errno; } DIR *dir = opendir(src); if (!dir) { return errno; } int rc = 0; for ( ; ; ) { errno = 0; struct dirent *entry = readdir(dir); if (!entry) { if (errno) { rc = errno; } break; } if ( !strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) { continue; } struct stat entry_st; char *src_path = path_join_path_alloc(src, entry->d_name, 0); char *dst_path = path_join_path_alloc(dst, entry->d_name, 0); if (lstat(src_path, &entry_st) != 0) { rc = errno; } else if (S_ISDIR(entry_st.st_mode)) { rc = _utils_copy_dir_recursive(src_path, dst_path); } else if (S_ISREG(entry_st.st_mode)) { rc = _utils_copy_regular(src_path, dst_path, &entry_st); } else if (S_ISLNK(entry_st.st_mode)) { rc = _utils_copy_symlink(src_path, dst_path, &entry_st); } else { // FIFO, socket, block/character device. rc = ENOTSUP; } free(src_path); free(dst_path); if (rc) { break; } } if (closedir(dir) != 0 && !rc) { rc = errno; } if ( created && chmod(dst, src_st.st_mode & 07777) != 0 && !rc) { rc = errno; } return rc; } int utils_copy_dir(const char *src, const char *dst) { if (!src || !*src || !dst || !*dst) { return AK_ERROR_INVALID_ARGS; } int rc = _utils_check_overlap(src, dst); return rc ? rc : _utils_copy_dir_recursive(src, dst); } int utils_copy_dir_to_parent(const char *src, const char *dst) { if (!src || !*src || !dst || !*dst) { return AK_ERROR_INVALID_ARGS; } struct pool *pool = pool_create_empty(); char *bname = path_basename(pool_strdup(pool, src)); dst = path_join_path_pool(pool, dst, bname, 0); int rv = utils_copy_dir(src, dst); pool_destroy(pool); return rv; } long int utils_strtol(const char *v, int base, int *rcp) { *rcp = 0; char *ep = 0; errno = 0; long int ret = strtol(v, &ep, base); if (*ep != '\0' || errno == ERANGE) { *rcp = AK_ERROR_INVALID_ARGS; return 0; } return ret; } long long utils_strtoll(const char *v, int base, int *rcp) { *rcp = 0; char *ep = 0; errno = 0; long long ret = strtoll(v, &ep, base); if ((*ep != '\0' && *ep != '\n') || errno == ERANGE) { *rcp = AK_ERROR_INVALID_ARGS; return 0; } return ret; } void utils_split_values_add(const char *v, struct xstr *xstr) { if (is_vlist(v)) { xstr_cat(xstr, v); return; } char buf[strlen(v) + 1]; const char *p = v; while (*p) { while (utils_char_is_space(*p)) ++p; if (*p == '\0') { break; } char *w = buf; char q = 0; while (*p && (q || !utils_char_is_space(*p))) { if (*p == '\\') { ++p; if (*p) { *w++ = *p++; } } else if (q) { if (*p == q) { q = 0; ++p; } else { *w++ = *p++; } } else if (*p == '\'' || *p == '"') { q = *p++; } else { *w++ = *p++; } } *w = '\0'; xstr_cat(xstr, "\1"); xstr_cat(xstr, buf); } } int utils_fd_make_non_blocking(int fd) { int rci, flags; while ((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR) ; if (flags == -1) { return errno; } while ((rci = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR) ; if (rci == -1) { return errno; } return 0; } int64_t utils_current_time_ms(void) { struct timespec ts; #if defined(CLOCK_REALTIME) if (clock_gettime(CLOCK_REALTIME, &ts) == -1) { akfatal(errno, "", 0); } #else struct timeval tv; gettimeofday(&tv, 0); return (int64_t) tv.tv_sec * 1000 + tv.tv_usec / 1000; #endif return (int64_t) ts.tv_sec * 1000 + ts.tv_nsec / 1000000; } const char* utils_json_escape_str(const char *val, ssize_t len, struct xstr *xstr) { if (!val || !xstr) { return 0; } if (len < 0) { len = strlen(val); } static const char *specials = "btnvfr"; xstr_cat2(xstr, "\"", 1); for (size_t i = 0; i < len; ++i) { uint8_t ch = (uint8_t) val[i]; if (ch == '"' || ch == '\'') { xstr_cat2(xstr, "\\", 1); xstr_cat2(xstr, &ch, 1); } else if (ch >= '\b' && ch <= '\r') { xstr_cat2(xstr, "\\", 1); xstr_cat2(xstr, &specials[ch - '\b'], 1); } else { xstr_cat2(xstr, &ch, 1); } } xstr_cat2(xstr, "\"", 1); return xstr_ptr(xstr); } #ifndef _AMALGAMATE_ #include "paths.h" #include "xstr.h" #include "alloc.h" #include "utils.h" #include #include #include #include #include #include #include #include #include #endif static inline uint64_t _ts_sec_nsec_to_ms(int64_t sec, int64_t nsec) { return sec * 1000ULL + utils_lround(nsec / 1.0e6); } bool path_is_absolute(const char *path) { if (!path) { return 0; } return *path == '/'; } bool path_is_accesible(const char *path, enum akpath_access a) { if (!path || *path == '\0') { return 0; } int mode = 0; if (a & AKPATH_READ) { mode |= R_OK; } if (a & AKPATH_WRITE) { mode |= W_OK; } if (a & AKPATH_EXEC) { mode |= X_OK; } if (mode == 0) { mode = F_OK; } return access(path, mode) == 0; } bool path_is_accesible_read(const char *path) { return path_is_accesible(path, AKPATH_READ); } bool path_is_accesible_write(const char *path) { return path_is_accesible(path, AKPATH_WRITE); } bool path_is_accesible_exec(const char *path) { return path_is_accesible(path, AKPATH_EXEC); } const char* path_real(const char *path, char buf[PATH_MAX]) { return realpath(path, buf); } const char* path_real_pool(const char *path, struct pool *pool) { char buf[PATH_MAX]; if (path_real(path, buf)) { return pool_strdup(pool, buf); } else { return 0; } } char* path_normalize(const char *path, char buf[PATH_MAX]) { char cwd[PATH_MAX]; if (!getcwd(cwd, PATH_MAX)) { akfatal(errno, "Cannot get CWD", 0); } return path_normalize_cwd(path, cwd, buf); } char* path_normalize_cwd(const char *path, const char *cwd, char buf[PATH_MAX]) { akassert(cwd); if (path[0] == '/') { utils_strncpy(buf, path, PATH_MAX); char *p = strchr(buf, '.'); if ( p == 0 || !(*(p - 1) == '/' && (p[1] == '/' || p[1] == '.' || p[1] == '\0'))) { return buf; } } else { utils_strncpy(buf, cwd, PATH_MAX); size_t len = strlen(buf); if (len < PATH_MAX - 1) { buf[len] = '/'; buf[len + 1] = '\0'; strncat(buf, path, PATH_MAX - len - 2); } else { errno = ENAMETOOLONG; akfatal(errno, "Failed to normalize path, name is too long: %s", path); } } // Normalize: split and process each segment const char *rp = buf; char *segments[PATH_MAX]; int top = 0; while (*rp) { while (*rp == '/') ++rp; if (!*rp) { break; } const char *sp = rp; while (*rp && *rp != '/') ++rp; size_t len = rp - sp; if (len == 0) { continue; } char segment[PATH_MAX]; memcpy(segment, sp, len); segment[len] = '\0'; if (strcmp(segment, ".") == 0) { continue; } else if (strcmp(segment, "..") == 0) { if (top > 0) { free(segments[--top]); } } else { segments[top++] = xstrdup(segment); } } if (top == 0) { buf[0] = '/'; buf[1] = '\0'; } else { buf[0] = '\0'; for (int i = 0; i < top; ++i) { strcat(buf, "/"); strcat(buf, segments[i]); free(segments[i]); } } return buf; } char* path_normalize_pool(const char *path, struct pool *pool) { char cwd[PATH_MAX]; if (!getcwd(cwd, PATH_MAX)) { akfatal(errno, "Cannot get CWD", 0); } return path_normalize_cwd_pool(path, cwd, pool); } char* path_normalize_cwd_pool(const char *path, const char *cwd, struct pool *pool) { if (cwd == 0) { return path_normalize_pool(path, pool); } char buf[PATH_MAX]; path_normalize_cwd(path, cwd, buf); return pool_strdup(pool, buf); } int path_mkdirs(const char *path) { if ((path[0] == '.' && path[1] == '\0') || path_is_exist(path)) { return 0; } int rc = 0; const size_t len = strlen(path); char buf[len + 1]; char *rp = buf; memcpy(rp, path, len + 1); for (char *p = rp + 1; *p; p++) { if (*p == '/') { *p = '\0'; if (mkdir(rp, S_IRWXU) != 0) { if (errno != EEXIST) { rc = errno; goto finish; } } *p = '/'; } } if (mkdir(rp, S_IRWXU) != 0) { if (errno != EEXIST) { rc = errno; goto finish; } } finish: return rc; } int path_mkdirs_for(const char *path) { char buf[PATH_MAX]; utils_strncpy(buf, path, sizeof(buf)); char *dir = path_dirname(buf); return path_mkdirs(dir); } static inline bool _is_autark_dist_root(const char *path) { return utils_endswith(path, "/.autark-dist"); } static void _rm_dir_recursive(const char *path) { struct stat st; if (lstat(path, &st)) { perror(path); return; } if (!S_ISDIR(st.st_mode)) { if (unlink(path) != 0) { perror(path); return; } return; } DIR *dir = opendir(path); if (!dir) { perror(path); return; } // Use dynamic allocation to avoid stack overflow early char *child = xmalloc(PATH_MAX); for (struct dirent *entry; (entry = readdir(dir)) != 0; ) { const char *name = entry->d_name; if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) { continue; } snprintf(child, PATH_MAX, "%s/%s", path, name); _rm_dir_recursive(child); } closedir(dir); free(child); if (rmdir(path)) { perror(path); } } int path_rm_cache(const char *path) { char resolved[PATH_MAX]; char child[PATH_MAX]; if (!path_is_exist(path)) { return 0; } if (!realpath(path, resolved)) { return errno; } DIR *dir = opendir(resolved); if (!dir) { return errno; } for (struct dirent *entry; (entry = readdir(dir)) != 0; ) { const char *name = entry->d_name; if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) { continue; } snprintf(child, sizeof(child), "%s/%s", path, name); if (!_is_autark_dist_root(child)) { _rm_dir_recursive(child); } } closedir(dir); return 0; } int path_rm_dir_recursive(const char *path) { char resolved[PATH_MAX]; char child[PATH_MAX]; if (!path_is_dir(path)) { return 0; } if (!realpath(path, resolved)) { return errno; } DIR *dir = opendir(resolved); if (!dir) { return errno; } for (struct dirent *entry; (entry = readdir(dir)) != 0; ) { const char *name = entry->d_name; if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) { continue; } snprintf(child, sizeof(child), "%s/%s", path, name); _rm_dir_recursive(child); } closedir(dir); return 0; } static int _stat(const char *path, int fd, struct akpath_stat *fs) { struct stat st = { 0 }; memset(fs, 0, sizeof(*fs)); if (path) { if (stat(path, &st)) { if (errno == ENOENT) { fs->ftype = AKPATH_NOT_EXISTS; return 0; } else { return errno; } } } else { if (fstat(fd, &st)) { if (errno == ENOENT) { fs->ftype = AKPATH_NOT_EXISTS; return 0; } else { return errno; } } } #if defined(__APPLE__) || defined(__NetBSD__) fs->atime = _ts_sec_nsec_to_ms(st.st_atime, st.st_atimensec); fs->mtime = _ts_sec_nsec_to_ms(st.st_mtime, st.st_mtimensec); fs->ctime = _ts_sec_nsec_to_ms(st.st_ctime, st.st_ctimensec); #else fs->atime = _ts_sec_nsec_to_ms(st.st_atim.tv_sec, st.st_atim.tv_nsec); fs->mtime = _ts_sec_nsec_to_ms(st.st_mtim.tv_sec, st.st_mtim.tv_nsec); fs->ctime = _ts_sec_nsec_to_ms(st.st_ctim.tv_sec, st.st_ctim.tv_nsec); #endif fs->size = (uint64_t) st.st_size; if (S_ISREG(st.st_mode)) { fs->ftype = AKPATH_TYPE_FILE; } else if (S_ISDIR(st.st_mode)) { fs->ftype = AKPATH_TYPE_DIR; } else if (S_ISLNK(st.st_mode)) { fs->ftype = AKPATH_LINK; } else { fs->ftype = AKPATH_OTHER; } return 0; } int path_stat(const char *path, struct akpath_stat *stat) { return _stat(path, -1, stat); } int path_stat_fd(int fd, struct akpath_stat *stat) { return _stat(0, fd, stat); } int path_stat_file(FILE *file, struct akpath_stat *stat) { return _stat(0, fileno(file), stat); } uint64_t path_mtime(const char *path) { struct akpath_stat st; if (path_stat(path, &st)) { return 0; } return st.mtime; } bool path_is_dir(const char *path) { struct akpath_stat st; if (path_stat(path, &st)) { return 0; } return st.ftype == AKPATH_TYPE_DIR; } bool path_is_file(const char *path) { struct akpath_stat st; if (path_stat(path, &st)) { return 0; } return st.ftype == AKPATH_TYPE_FILE; } bool path_is_exist(const char *path) { struct akpath_stat st; if (path_stat(path, &st)) { return 0; } return st.ftype != AKPATH_NOT_EXISTS; } static inline int _path_num_segments(const char *path) { int c = 0; for (const char *rp = path; *rp != '\0'; ++rp) { if (*rp == '/' || *rp == '\0') { ++c; } } return c; } char* path_relativize(const char *from, const char *to) { char cwd[PATH_MAX]; if (!getcwd(cwd, PATH_MAX)) { akfatal(errno, "Cannot get CWD", 0); } return path_relativize_cwd(from, to, cwd); } char* path_relativize_cwd(const char *from_, const char *to_, const char *cwd) { char from[PATH_MAX], to[PATH_MAX]; if (from_ == 0) { from_ = cwd; } path_normalize_cwd(from_, cwd, from); path_normalize_cwd(to_, cwd, to); akassert(*from == '/' && *to == '/'); int sf, sc; struct xstr *xstr = xstr_create_empty(); const char *frp = from, *trp = to, *srp = to; for (++frp, ++trp, sc = 0, sf = _path_num_segments(frp - 1); *frp == *trp; ++frp, ++trp) { if (*frp == '/' || *frp == '\0') { ++sc; srp = trp; if (*frp == '\0') { break; } } } if ((*frp == '\0' && *trp == '/') || (*frp == '/' && *trp == '\0')) { ++sc; srp = trp; } for (int i = 0, l = sf - sc; i < l; ++i) { xstr_cat2(xstr, "../", AK_LLEN("../")); } if (*srp != '\0') { xstr_cat(xstr, srp + 1); } return xstr_destroy_keep_ptr(xstr); } char* path_dirname(char *path) { return dirname(path); } char* path_basename(char *path) { size_t i; if (!path || *path == '\0') { return "."; } i = strlen(path) - 1; for ( ; i && path[i] == '/'; i--) path[i] = 0; for ( ; i && path[i - 1] != '/'; i--) ; return path + i; } const char* path_is_prefix_for(const char *prefix, const char *path, const char *cwd) { akassert(path_is_absolute(prefix)); char buf[PATH_MAX]; if (!path_is_absolute(path)) { char cwdbuf[PATH_MAX]; if (!cwd) { cwd = getcwd(cwdbuf, sizeof(cwdbuf)); } path = path_normalize_cwd(path, cwd, buf); } int len = strlen(prefix); if (len < 1 || !utils_startswith(path, prefix)) { return 0; } const char *p = path + len; if (*p == '/') { while (*p == '/') ++p; return p; } else if (prefix[len - 1] == '/') { return p; } else { return 0; } } char* path_join_path_alloc(const char *dir, const char *name, char **out) { while (*name == '/') ++name; size_t dl = strlen(dir); size_t nl = strlen(name); size_t slash = dl && dir[dl - 1] != '/'; char *path = xmalloc(dl + slash + nl + 1); memcpy(path, dir, dl); if (slash) { path[dl++] = '/'; } memcpy(path + dl, name, nl + 1); if (out) { *out = path; } return path; } const char* path_join_path_pool(struct pool *pool, const char *dir, const char *name, const char **out) { while (*name == '/') ++name; size_t dl = strlen(dir); size_t nl = strlen(name); size_t slash = dl && dir[dl - 1] != '/'; char *path = pool_alloc(pool, dl + slash + nl + 1); memcpy(path, dir, dl); if (slash) { path[dl++] = '/'; } memcpy(path + dl, name, nl + 1); if (out) { *out = path; } return path; } #ifndef _AMALGAMATE_ #include "spawn.h" #include "log.h" #include "ulist.h" #include "pool.h" #include "xstr.h" #include "env.h" #include "utils.h" #include #include #include #include #include #include #include #endif extern char **environ; struct spawn { pid_t pid; int wstatus; void *user_data; struct pool *pool; struct ulist args; struct ulist env; const char *exec; char *path_overriden; bool nowait; size_t (*stdin_provider)(char *buf, size_t buflen, struct spawn*); void (*stdout_handler)(char *buf, size_t buflen, struct spawn*); void (*stderr_handler)(char *buf, size_t buflen, struct spawn*); }; struct spawn* spawn_create(const char *exec, void *user_data) { akassert(exec); struct pool *pool = pool_create_empty(); struct spawn *s = pool_alloc(pool, sizeof(*s)); *s = (struct spawn) { .pool = pool, .pid = -1, .args = { .usize = sizeof(char*) }, .env = { .usize = sizeof(char*) }, .user_data = user_data, }; s->exec = spawn_arg_add(s, exec); if (g_env.project.cache_dir) { spawn_env_path_prepend(s, g_env.project.cache_dir); } if (g_env.spawn.extra_env_paths) { spawn_env_path_prepend(s, g_env.spawn.extra_env_paths); } return s; } void spawn_visit_cmd(struct spawn *s, void *user_data, void (*visitor)(int num, const char *arg, void*)) { for (int i = 0; i < s->args.num; ++i) { const char *arg = *(const char**) ulist_get(&s->args, i); visitor(i, arg, user_data); } } static const char* _spawn_arg_add(struct spawn *s, const char *arg, int len) { if (!arg || *arg == '\0') { return ""; } if (is_vlist(arg)) { struct vlist_iter iter; vlist_iter_init(arg, &iter); while (vlist_iter_next(&iter)) { _spawn_arg_add(s, iter.item, iter.len); } return arg; } else { const char *v = (len < 0) ? pool_strdup(s->pool, arg) : pool_strndup(s->pool, arg, len); ulist_push(&s->args, &v); return v; } } const char* spawn_arg_add(struct spawn *s, const char *arg) { return _spawn_arg_add(s, arg, -1); } bool spawn_arg_exists(struct spawn *s, const char *arg) { for (int i = 0; i < s->args.num; ++i) { const char *v = *(char**) ulist_get(&s->args, i); if (strcmp(v, arg) == 0) { return true; } } return false; } bool spawn_arg_starts_with(struct spawn *s, const char *arg) { for (int i = 0; i < s->args.num; ++i) { const char *v = *(char**) ulist_get(&s->args, i); if (utils_startswith(v, arg)) { return true; } } return false; } void spawn_env_set(struct spawn *s, const char *key, const char *val) { akassert(key); if (!val) { val = ""; } const char *v = pool_printf(s->pool, "%s=%s", key, val); ulist_push(&s->env, &v); } void spawn_env_path_prepend(struct spawn *s, const char *path) { struct xstr *xstr = xstr_create_empty(); const char *old = s->path_overriden; if (!old) { old = getenv("PATH"); } if (old) { xstr_printf(xstr, "%s:%s", path, old); } else { xstr_cat(xstr, path); } free(s->path_overriden); s->path_overriden = xstr_destroy_keep_ptr(xstr); } static char* _file_resolve_in_path(struct spawn *s, const char *file, char pathbuf[PATH_MAX]) { char buf[PATH_MAX]; const char *sp = s->path_overriden; if (!sp) { sp = getenv("PATH"); } const char *ep = sp; while (sp) { for ( ; *ep != '\0' && *ep != ':'; ++ep) ; if (ep - sp > 0 && ep - sp < PATH_MAX) { memcpy(buf, sp, ep - sp); buf[ep - sp] = '\0'; if (buf[ep - sp - 1] != '/') { snprintf(pathbuf, PATH_MAX, "%s/%s", buf, file); } else { snprintf(pathbuf, PATH_MAX, "%s%s", buf, file); } if (!access(pathbuf, F_OK)) { return pathbuf; } } while (*ep == ':') ++ep; if (*ep == '\0') { break; } sp = ep; } return 0; } static char* _env_find(struct spawn *s, const char *key, size_t keylen) { if (keylen == 0) { return 0; } for (int i = 0; i < s->env.num; ++i) { char *v = *(char**) ulist_get(&s->env, i); if (strncmp(v, key, keylen) == 0 && v[keylen] == '=') { ulist_remove(&s->env, i); // Entry is not needed anymore. return v; } } return 0; } static char** _env_create(struct spawn *s) { int i = 0, c = 0; if (s->path_overriden) { spawn_env_set(s, "PATH", s->path_overriden); } for (char **ep = environ; *ep; ++ep, ++c) ; c += s->env.num; char **nenv = pool_alloc(s->pool, sizeof(*nenv) * (c + 1)); nenv[c] = 0; for (char **it = environ; *it && i < c; ++it, ++i) { const char *entry = *it; const char *ep = strchr(entry, '='); if (ep == 0) { continue; } size_t keylen = ep - entry; char *my = _env_find(s, entry, keylen); if (my) { nenv[i] = my; } else { nenv[i] = (char*) pool_strdup(s->pool, entry); } } for (c = 0; c < s->env.num; ++c, ++i) { nenv[i] = *(char**) ulist_get(&s->env, c); } nenv[i] = 0; return nenv; } static char** _args_create(struct spawn *s) { char **args = pool_alloc(s->pool, sizeof(*args) * (s->args.num + 1)); for (int i = 0; i < s->args.num; ++i) { char *v = *(char**) ulist_get(&s->args, i); args[i] = v; } args[s->args.num] = 0; return args; } void* spawn_user_data(struct spawn *s) { return s->user_data; } pid_t spawn_pid(struct spawn *s) { return s->pid; } int spawn_exit_code(struct spawn *s) { if (WIFEXITED(s->wstatus)) { return WEXITSTATUS(s->wstatus); } return -1; } void spawn_set_stdin_provider( struct spawn *s, size_t (*provider)(char *buf, size_t buflen, struct spawn*)) { s->stdin_provider = provider; } void spawn_set_stdout_handler( struct spawn *s, void (*handler)(char *buf, size_t buflen, struct spawn*)) { s->stdout_handler = handler; } static void _default_stdout_handler(char *buf, size_t buflen, struct spawn *s) { fprintf(stdout, "%s: %s", s->exec, buf); } static void _default_stderr_handler(char *buf, size_t buflen, struct spawn *s) { fprintf(stderr, "%s: %s", s->exec, buf); } void spawn_set_stderr_handler( struct spawn *s, void (*handler)(char *buf, size_t buflen, struct spawn*)) { s->stderr_handler = handler; } void spawn_set_nowait(struct spawn *s, bool nowait) { s->nowait = nowait; } void spawn_set_wstatus(struct spawn *s, int wstatus) { s->wstatus = wstatus; } int spawn_do(struct spawn *s) { int rc = 0; bool nowait = s->nowait; if (!s->stderr_handler) { s->stderr_handler = _default_stderr_handler; } if (!s->stdout_handler) { s->stdout_handler = _default_stdout_handler; } int pipe_stdout[2] = { -1, -1 }; int pipe_stderr[2] = { -1, -1 }; int pipe_stdin[2] = { -1, -1 }; char **envp = _env_create(s); char **args = _args_create(s); const char *file = args[0]; { struct xstr *xstr = xstr_create_empty(); for (char **a = args; *a; ++a) { if (a != args) { xstr_cat(xstr, " "); } xstr_cat(xstr, *a); } puts(xstr_ptr(xstr)); xstr_destroy(xstr); } char buf[1024]; char pathbuf[PATH_MAX]; ssize_t len; if (!strchr(file, '/')) { const char *rfile = _file_resolve_in_path(s, file, pathbuf); if (!rfile) { akerror(ENOENT, "Failed to find: '%s' in PATH", file); errno = ENOENT; return errno; } file = rfile; } if (!nowait) { if (pipe(pipe_stdout) == -1) { return errno; } if (pipe(pipe_stderr) == -1) { return errno; } } if (s->stdin_provider) { if (pipe(pipe_stdin) == -1) { return errno; } } s->pid = fork(); if (s->pid == -1) { return errno; } if (s->pid == 0) { if (pipe_stdin[0] != -1) { close(pipe_stdin[1]); if (dup2(pipe_stdin[0], STDIN_FILENO) == -1) { perror("dup2"); _exit(EXIT_FAILURE); } close(pipe_stdin[0]); } if (!nowait) { close(pipe_stdout[0]); if (dup2(pipe_stdout[1], STDOUT_FILENO) == -1) { perror("dup2"); _exit(EXIT_FAILURE); } close(pipe_stdout[1]); close(pipe_stderr[0]); if (dup2(pipe_stderr[1], STDERR_FILENO) == -1) { perror("dup2"); _exit(EXIT_FAILURE); } close(pipe_stderr[1]); } execve(file, args, envp); perror("execve"); _exit(EXIT_FAILURE); } else { if (!nowait) { close(pipe_stdout[1]); close(pipe_stderr[1]); } if (s->stdin_provider) { close(pipe_stdin[0]); ssize_t tow = 0; while ((tow = s->stdin_provider(buf, sizeof(buf), s)) > 0) { while (tow > 0) { len = write(pipe_stdin[1], buf, tow); if (len == -1 && errno == EAGAIN) { continue; } if (len > 0) { tow -= len; } else { break; } } } close(pipe_stdin[1]); } if (!nowait) { // Now read both stdout & stderr struct pollfd fds[] = { { .fd = pipe_stdout[0], .events = POLLIN }, { .fd = pipe_stderr[0], .events = POLLIN } }; int c = sizeof(fds) / sizeof(fds[0]); while (c > 0) { int ret = poll(fds, sizeof(fds) / sizeof(fds[0]), -1); if (ret == -1) { break; } for (int i = 0; i < sizeof(fds) / sizeof(fds[0]); ++i) { if (fds[i].fd == -1) { continue; } short revents = fds[i].revents; if (revents & POLLIN) { ssize_t n = read(fds[i].fd, buf, sizeof(buf) - 1); if (n > 0) { buf[n] = '\0'; if (fds[i].fd == pipe_stdout[0]) { s->stdout_handler(buf, n, s); } else { s->stderr_handler(buf, n, s); } } else if (n == -1 && errno != EAGAIN && errno != EWOULDBLOCK) { close(fds[i].fd); fds[i].fd = -1; --c; } } if (revents & (POLLERR | POLLHUP | POLLNVAL)) { close(fds[i].fd); fds[i].fd = -1; --c; } } } if (waitpid(s->pid, &s->wstatus, 0) == -1) { perror("waitpid"); } } } if (rc) { akerror(rc, "Failed to spawn: %s", file); } return rc; } void spawn_destroy(struct spawn *s) { if (s) { free(s->path_overriden); ulist_destroy_keep(&s->args); ulist_destroy_keep(&s->env); pool_destroy(s->pool); } } #ifndef _AMALGAMATE_ #include "deps.h" #include "log.h" #include "utils.h" #include "paths.h" #include "env.h" #include #include #include #include #include #include #endif int deps_open(const char *path, int omode, struct deps *d) { int rc = 0; akassert(path && d); memset(d, 0, sizeof(*d)); d->file = fopen(path, (omode & DEPS_OPEN_TRUNCATE) ? "w+" : ((omode & DEPS_OPEN_READONLY) ? "r" : "a+")); if (!d->file) { return errno; } if (fseek(d->file, 0, SEEK_SET) < 0) { return errno; } return rc; } bool deps_cur_next(struct deps *d) { int rc; if (d && d->file) { if (!fgets(d->buf, sizeof(d->buf), d->file)) { return false; } char *ls = 0; char *rp = d->buf; d->type = *rp++; d->flags = *rp++; if (d->type == DEPS_TYPE_NODE_VALUE || d->type == DEPS_TYPE_ENV || d->type == DEPS_TYPE_SYS_ENV) { utils_chars_replace(d->buf, '\2', '\n'); } if (d->type == DEPS_TYPE_ALIAS || d->type == DEPS_TYPE_ENV || d->type == DEPS_TYPE_SYS_ENV) { d->alias = rp; d->resource = 0; } else { d->resource = rp; d->alias = 0; } while (*rp) { if (*rp == '\1') { if (!d->resource) { *rp = '\0'; d->resource = rp + 1; } ls = rp; } ++rp; } if (ls) { *ls = '\0'; if (d->type == DEPS_TYPE_FILE || d->type == DEPS_TYPE_NODE_VALUE || d->type == DEPS_TYPE_ALIAS) { ++ls; d->serial = utils_strtoll(ls, 10, &rc); if (rc) { akerror(rc, "Failed to read '%s' as number", ls); return false; } } } return true; } return false; } bool deps_cur_is_outdated(struct node *n, struct deps *d) { if (d) { switch (d->type) { case DEPS_TYPE_FILE: { struct akpath_stat st; if (path_stat(d->resource, &st) || st.ftype == AKPATH_NOT_EXISTS || st.mtime > d->serial) { return true; } break; } case DEPS_TYPE_ALIAS: { struct akpath_stat st; if (path_stat(d->alias, &st) || st.ftype == AKPATH_NOT_EXISTS || st.mtime > d->serial) { return true; } break; } case DEPS_TYPE_ENV: { const char *val = unit_env_get(n, d->alias); if (!val) { val = ""; } return strcmp(val, d->resource) != 0; } case DEPS_TYPE_SYS_ENV: { const char *val = getenv(d->alias); if (!val) { val = ""; } return strcmp(val, d->resource) != 0; } case DEPS_TYPE_FILE_NOT_EXISTS: { struct akpath_stat st; if (path_stat(d->resource, &st) || st.ftype == AKPATH_NOT_EXISTS) { return true; } break; } case DEPS_TYPE_OUTDATED: return true; } } return false; } static int _deps_add(struct deps *d, char type, char flags, const char *resource, const char *alias, int64_t serial) { int rc = 0; char buf[2][PATH_MAX]; char dbuf[DEPS_BUF_SZ]; if (flags == 0) { flags = ' '; } if (type == DEPS_TYPE_FILE) { path_normalize(resource, buf[0]); resource = buf[0]; struct akpath_stat st; if (!path_stat(resource, &st) && st.ftype != AKPATH_NOT_EXISTS) { serial = st.mtime; } } else if (type == DEPS_TYPE_ALIAS) { path_normalize(resource, buf[0]); path_normalize(alias, buf[1]); resource = buf[0]; alias = buf[1]; struct akpath_stat st; if (!path_stat(alias, &st) && st.ftype != AKPATH_NOT_EXISTS) { serial = st.mtime; } } else if (type == DEPS_TYPE_ENV || type == DEPS_TYPE_NODE_VALUE || type == DEPS_TYPE_SYS_ENV) { assert(resource); utils_strncpy(dbuf, resource, sizeof(dbuf)); utils_chars_replace(dbuf, '\n', '\2'); resource = dbuf; } else if (type == DEPS_TYPE_FILE_OUTDATED) { type = DEPS_TYPE_FILE; path_normalize(resource, buf[0]); resource = buf[0]; serial = 0; } else if (type == DEPS_TYPE_FILE_NOT_EXISTS) { path_normalize(resource, buf[0]); resource = buf[0]; serial = 0; } long int off = ftell(d->file); if (off < 0) { return errno; } fseek(d->file, 0, SEEK_END); if (type != DEPS_TYPE_ALIAS && type != DEPS_TYPE_ENV && type != DEPS_TYPE_SYS_ENV) { if (fprintf(d->file, "%c%c%s\1%" PRId64 "\n", type, flags, resource, serial) < 0) { rc = errno; } } else { if (fprintf(d->file, "%c%c%s\1%s\1%" PRId64 "\n", type, flags, alias, resource, serial) < 0) { rc = errno; } } fseek(d->file, off, SEEK_SET); if (!rc) { ++d->num_registered; } return rc; } int deps_add(struct deps *d, char type, char flags, const char *resource, int64_t serial) { return _deps_add(d, type, flags, resource, 0, serial); } int deps_add_alias(struct deps *d, char flags, const char *resource, const char *alias) { return _deps_add(d, DEPS_TYPE_ALIAS, flags, resource, alias, 0); } int deps_add_env(struct deps *d, char flags, const char *key, const char *value) { return _deps_add(d, DEPS_TYPE_ENV, flags, value, key, 0); } int deps_add_sys_env(struct deps *d, char flags, const char *key, const char *value) { return _deps_add(d, DEPS_TYPE_SYS_ENV, flags, value, key, 0); } void deps_close(struct deps *d) { if (d && d->file) { fclose(d->file); d->file = 0; } } void deps_prune_all(const char *path) { unlink(path); } #ifndef _AMALGAMATE_ #include "log.h" #include "fetchreg.h" #include "alloc.h" #include "utils.h" #include #include #endif struct fetchreg { FILE *f; }; static void _fetchreg_destroy(struct fetchreg *r) { if (r) { if (r->f) { fclose(r->f); } free(r); } } int fetchreg_open(const char *path, struct fetchreg **out) { akassert(path && out); FILE *f = fopen(path, "a+"); if (!f) { *out = 0; return errno; } struct fetchreg *r = xmalloc(sizeof(*r)); r->f = f; *out = r; return 0; } bool fetchreg_find( struct fetchreg *r, const char *url, void *user_data, void (*cb)(const struct fetcherg_entry*, void*)) { akassert(r && url); char buf[PATH_MAX * 3 + 2]; char sbuf[sizeof(buf)]; struct fetcherg_entry se = { 0 }; if (fseek(r->f, 0, SEEK_SET) == -1) { akerror(errno, "fetchreg for %s", url); return false; } // Find the last matched fetched entry while (fgets(buf, sizeof(buf), r->f)) { struct fetcherg_entry e = { 0 }; int idxs[1] = { 0 }; char *rp = buf; for ( ; *rp; ++rp) { if (*rp == '\1') { *rp = '\0'; if (strcmp(buf, url) != 0) { break; } e.url = buf; if (rp[1] != '\0' && rp[1] != '\n') { e.target = rp + 1; idxs[0] = e.target - buf; } break; } } if (e.url) { memset(&se, 0, sizeof(se)); memcpy(sbuf, buf, sizeof(buf)); se.url = sbuf; if (idxs[0]) { char *target = sbuf + idxs[0]; int rv = utils_endswith(target, "\n"); if (rv) { target[rv - 1] = '\0'; } se.target = target; } } } if (se.url) { if (cb) { cb(&se, user_data); } return true; } else { return false; } } int fetchreg_register(struct fetchreg *r, const struct fetcherg_entry *entry) { if (!r || !entry || !entry->url) { return AK_ERROR_INVALID_ARGS; } long int old_pos = ftell(r->f); if (fseek(r->f, SEEK_END, 0) == -1) { return errno; } if (entry->target) { if (fprintf(r->f, "%s\1%s\n", entry->url, entry->target) < 0) { return errno; } } else { if (fprintf(r->f, "%s\1\n", entry->url) < 0) { return errno; } } fseek(r->f, old_pos, SEEK_SET); fflush(r->f); return 0; } void fetchreg_close(struct fetchreg *r) { _fetchreg_destroy(r); } #ifndef _AMALGAMATE_ #include "script.h" #endif int node_script_setup(struct node *n) { return 0; } #ifndef _AMALGAMATE_ #include "script.h" #include "env.h" #include "log.h" #include "pool.h" #include "paths.h" #include "spawn.h" #include "deps.h" #include #include #include #endif static void _check_stdout_handler(char *buf, size_t buflen, struct spawn *s) { fprintf(stdout, "%s", buf); } static void _check_stderr_handler(char *buf, size_t buflen, struct spawn *s) { fprintf(stderr, "%s", buf); } static void _check_on_env_value(struct node_resolve *nr, const char *key, const char *val) { struct unit *unit = nr->user_data; akassert(unit->n); if (g_env.verbose) { akinfo("%s %s=%s", unit->rel_path, key, val); } node_env_set(unit->n, key, val); } static void _check_on_resolve(struct node_resolve *r) { struct unit *unit = r->user_data; struct node *n = unit->n; const char *path = unit->impl; // Good, now add dependency on itself struct deps deps; int rc = deps_open(r->deps_path_tmp, 0, &deps); if (rc) { node_fatal(rc, unit->n, "Failed to open depencency file: %s", r->deps_path_tmp); } struct spawn *s = spawn_create(path, unit); spawn_set_stdout_handler(s, _check_stdout_handler); spawn_set_stderr_handler(s, _check_stderr_handler); for (struct node *nn = n->child; nn; nn = nn->next) { if (node_is_can_be_value(nn)) { spawn_arg_add(s, node_value(nn)); } } rc = spawn_do(s); if (rc) { node_fatal(rc, n, "%s", path); } else { int code = spawn_exit_code(s); if (code != 0) { node_fatal(AK_ERROR_EXTERNAL_COMMAND, n, "%s: %d", path, code); } } spawn_destroy(s); deps_add(&deps, DEPS_TYPE_FILE, 0, path, 0); for (int i = 0; i < r->node_val_deps.num; ++i) { struct node *nv = *(struct node**) ulist_get(&r->node_val_deps, i); const char *val = node_value(nv); if (val) { deps_add(&deps, DEPS_TYPE_NODE_VALUE, 0, val, i); } } deps_close(&deps); } static char* _resolve_check_path(struct pool *pool, struct node *n, const char *script, struct unit **out_u) { *out_u = 0; char buf[PATH_MAX]; for (int i = (int) g_env.stack_units.num - 1; i >= 0; --i) { struct unit_ctx *c = (struct unit_ctx*) ulist_get(&g_env.stack_units, i); struct unit *u = c->unit; snprintf(buf, sizeof(buf), "%s/.autark/%s", u->dir, script); if (path_is_exist(buf)) { *out_u = u; return pool_strdup(pool, buf); } } node_fatal(AK_ERROR_FAIL, n, "Check script not found: %s", script); return 0; } static void _check_script(struct node *n) { const char *script = node_value(n); if (g_env.verbose) { node_info(n->parent, "%s", script); } struct unit *parent = 0; struct pool *pool = pool_create(on_unit_pool_destroy); char *path = _resolve_check_path(pool, n, script, &parent); const char *vpath = pool_printf(pool, "%s/.autark/%s", parent->dir, n->vfile); struct unit *unit = unit_create(vpath, 0, pool); unit->n = n; unit->impl = path; unit_push(unit, n); struct node_resolve r = { .n = n, .mode = NODE_RESOLVE_ENV_ALWAYS, .path = n->vfile, .user_data = unit, .on_env_value = _check_on_env_value, .on_resolve = _check_on_resolve, .node_val_deps = { .usize = sizeof(struct node*) }, }; for (struct node *nn = n->child; nn; nn = nn->next) { if (node_is_value_may_be_dep_saved(nn, 0)) { ulist_push(&r.node_val_deps, &nn); } } node_resolve(&r); unit_pop(); pool_destroy(pool); } static void _check_init(struct node *n) { for (struct node *nn = n->child; nn; nn = nn->next) { _check_script(nn); } } int node_check_setup(struct node *n) { n->init = _check_init; return 0; } #ifndef _AMALGAMATE_ #include "script.h" #include "xstr.h" #include "utils.h" #include "alloc.h" #include "env.h" #endif static const char* _set_value_get(struct node *n); static void _set_dispose(struct node *n) { if ((uintptr_t) n->impl != (uintptr_t) -1) { free(n->impl); } n->impl = 0; } static bool _set_is_let(struct node *n) { return strcmp(n->value, "let") == 0; } static struct unit* _unit_for_set(struct node *n, struct node *nn, const char **keyp) { if (nn->type == NODE_TYPE_BAG) { if (strcmp(nn->value, "root") == 0) { *keyp = node_value(nn->child); return unit_root(); } else if (strcmp(nn->value, "parent") == 0) { *keyp = node_value(nn->child); return unit_parent(n); } } else { *keyp = node_value(nn); } return unit_peek(); } static void _set_init_impl(struct node *n) { const char *key = 0; struct unit *unit = n->child ? _unit_for_set(n, n->child, &key) : 0; if (!key) { node_warn(n, "No name specified for 'set' directive"); return; } unsigned tag = 0; struct node *nn = unit_env_get_node(unit, key, &tag); if (nn && nn != n) { n->recur_next.n = nn; } unit_env_set_node(unit, key, n, 0); } static void _set_init(struct node *n) { _set_init_impl(n); } static void _set_setup(struct node *n) { if (_set_is_let(n)) { _set_init_impl(n); } if (n->child && strcmp(n->value, "env") == 0) { const char *v = _set_value_get(n); if (v) { const char *key = 0; _unit_for_set(n, n->child, &key); if (key) { if (g_env.verbose) { node_info(n, "%s=%s", key, v); } setenv(key, v, 1); } } } } static void _set_build(struct node *n) { if (_set_is_let(n)) { _set_init_impl(n); } } static const char* _set_value_get(struct node *n) { if (n->recur_next.active && n->recur_next.n) { return _set_value_get(n->recur_next.n); } n->recur_next.active = true; struct node_foreach *fe = node_find_parent_foreach(n); if (fe || _set_is_let(n)) { if ((uintptr_t) n->impl != (uintptr_t) -1) { free(n->impl); } n->impl = 0; } if ((uintptr_t) n->impl == (uintptr_t) -1) { n->recur_next.active = false; return 0; } else if (n->impl) { n->recur_next.active = false; return n->impl; } n->impl = (void*) (uintptr_t) -1; struct node *nn = n->child->next; if (!nn) { n->impl = xstrdup(""); n->recur_next.active = false; return n->impl; } if (!nn->next && nn->value[0] != '.') { // Single value const char *v = node_value(nn); n->impl = xstrdup(v); n->recur_next.active = false; return n->impl; } struct xstr *xstr = xstr_create_empty(); for ( ; nn; nn = nn->next) { const char *v = node_value(nn); if (!v) { v = ""; } if (nn->value[0] == '.' && nn->value[1] == '.') { utils_split_values_add(v, xstr); } else { if (!is_vlist(v)) { xstr_cat(xstr, "\1"); } xstr_cat(xstr, v); } } n->impl = xstr_destroy_keep_ptr(xstr); n->recur_next.active = false; return n->impl; } int node_set_setup(struct node *n) { n->flags |= NODE_FLG_NO_CWD; n->init = _set_init; n->setup = _set_setup; n->build = _set_build; n->value_get = _set_value_get; n->dispose = _set_dispose; return 0; } #ifndef _AMALGAMATE_ #include "script.h" #include "alloc.h" #include "spawn.h" #include "xstr.h" #include "utils.h" #include #endif static const char* _subst_setval(struct node *n, const char *vv, const char *dv) { if (vv) { if (n->impl) { if (strcmp(n->impl, vv) == 0) { return n->impl; } free(n->impl); } n->impl = xstrdup(vv); return n->impl; } else { return dv; } } static const char* _subst_value(struct node *n) { if (n->child) { const char *dv_ = node_value(n->child->next); const char *dv = dv_; if (!dv) { dv = ""; } const char *key = node_value(n->child); if (!key) { node_warn(n, "No key specified"); return _subst_setval(n, 0, dv); } struct node_foreach *fe = node_find_parent_foreach(n); if (fe && strcmp(fe->name, key) == 0) { ++fe->access_cnt; return _subst_setval(n, fe->value, dv); } const char *vv = node_env_get(n, key); return _subst_setval(n, vv, dv); } return ""; } static void _subst_dispose(struct node *n) { if (n->impl) { free(n->impl); } } struct _subst_ctx { struct node *n; struct xstr *xstr; const char *cmd; }; static void _subst_stderr_handler(char *buf, size_t buflen, struct spawn *s) { fprintf(stderr, "%s", buf); } static void _subst_stdout_handler(char *buf, size_t buflen, struct spawn *s) { struct _subst_ctx *ctx = spawn_user_data(s); xstr_cat2(ctx->xstr, buf, buflen); fprintf(stdout, "%s", buf); } static const char* _subst_value_proc(struct node *n) { if (n->impl) { return n->impl; } const char *cmd = node_value(n->child); if (cmd == 0 || *cmd == '\0') { return 0; } struct xstr *xstr = xstr_create_empty(); struct _subst_ctx ctx = { .n = n, .cmd = cmd, .xstr = xstr }; struct spawn *s = spawn_create(cmd, &ctx); spawn_set_stdout_handler(s, _subst_stdout_handler); spawn_set_stderr_handler(s, _subst_stderr_handler); for (struct node *nn = n->child->next; nn; nn = nn->next) { if (node_is_can_be_value(nn)) { spawn_arg_add(s, node_value(nn)); } } int rc = spawn_do(s); if (rc) { node_fatal(rc, n, "%s", cmd); } else { int code = spawn_exit_code(s); if (code != 0) { node_fatal(AK_ERROR_EXTERNAL_COMMAND, n, "%s: %d", cmd, code); } } char *val = xstr_destroy_keep_ptr(xstr); for (int i = strlen(val) - 1; i >= 0 && utils_char_is_space(val[i]); --i) { val[i] = '\0'; } n->impl = val; spawn_destroy(s); return n->impl; } static void _subst_value_proc_cache_on_resolve(struct node_resolve *r) { struct node *n = r->n; _subst_value_proc(n); if (!n->impl) { n->impl = xstrdup(""); } struct unit *unit = unit_peek(); const char *path = pool_printf(r->pool, "%s/%s.cache", unit->cache_dir, r->path); int rc = utils_file_write_buf(path, (char*) n->impl, strlen(n->impl), false); if (rc) { node_fatal(rc, n, "Failed to write file: %s", path); } struct deps deps; rc = deps_open(r->deps_path_tmp, 0, &deps); if (rc) { node_fatal(rc, n, "Failed to open dependency file: %s", r->deps_path_tmp); } node_add_unit_deps(n, &deps); deps_add(&deps, DEPS_TYPE_FILE, 0, path, 0); deps_close(&deps); } static const char* _subst_value_proc_cache(struct node *n) { if (n->impl) { return n->impl; } struct node_resolve r = { .n = n, .path = n->vfile, .on_resolve = _subst_value_proc_cache_on_resolve, .node_val_deps = { .usize = sizeof(struct node*) }, }; for (struct node *cn = n->child; cn; cn = cn->next) { if (node_is_value_may_be_dep_saved(cn, NODE_TYPE_VALUE)) { ulist_push(&r.node_val_deps, &cn); } } node_resolve(&r); if (!n->impl) { struct unit *unit = unit_peek(); char path[PATH_MAX]; snprintf(path, sizeof(path), "%s/%s.cache", unit->cache_dir, r.path); struct value val = utils_file_as_buf(path, 1024 * 1024); // 1Mb max if (val.error) { node_fatal(val.error, n, "Failed to read cache file: %s", path); } n->impl = val.buf; } return n->impl; } int node_subst_setup(struct node *n) { if (strstr(n->value, "@@")) { n->value_get = _subst_value_proc_cache; } else if (strchr(n->value, '@')) { n->value_get = _subst_value_proc; } else { n->flags |= NODE_FLG_NO_CWD; n->value_get = _subst_value; } n->dispose = _subst_dispose; return 0; } #ifndef _AMALGAMATE_ #include "script.h" #include "log.h" #include "env.h" #include "utils.h" #include #endif static bool _if_defined_eval(struct node *mn) { struct node *n = mn->parent; for (struct node *nn = mn->child; nn; nn = nn->next) { const char *val = node_value(mn->child); if (val && *val != '\0' && node_env_get(n, val)) { return true; } } return false; } static bool _if_matched_eval(struct node *mn) { const char *val1 = node_value(mn->child); const char *val2 = mn->child ? node_value(mn->child->next) : 0; if (val1 && val2) { return strcmp(val1, val2) == 0; } else if (val1 == 0 && val2 == 0) { return true; } else { return false; } } static bool _if_prefix_eval(struct node *mn) { const char *val1 = node_value(mn->child); const char *val2 = mn->child ? node_value(mn->child->next) : 0; if (val1 && val2) { return utils_startswith(val1, val2); } else if (val1 == 0 && val2 == 0) { return true; } else { return false; } } static bool _if_contains_eval(struct node *mn) { const char *val1 = node_value(mn->child); const char *val2 = mn->child ? node_value(mn->child->next) : 0; if (val1 && val2) { return strstr(val1, val2); } else if (val1 == 0 && val2 == 0) { return true; } else { return false; } } static bool _if_in_eval(struct node *mn) { if (mn->child) { const char *val1 = node_value(mn->child); for (struct node *nn = mn->child->next; nn; nn = nn->next) { const char *val2 = node_value(nn); if (val1 && val2) { return strcmp(val1, val2) == 0; } else if (val1 == 0 && val2 == 0) { return true; } } } return false; } static bool _if_cond_eval(struct node *n, struct node *mn); static bool _if_OR_eval(struct node *n, struct node *mn) { for (struct node *nn = mn->child; nn; nn = nn->next) { if (_if_cond_eval(n, nn)) { return true; } } return false; } static bool _if_AND_eval(struct node *n, struct node *mn) { for (struct node *nn = mn->child; nn; nn = nn->child) { if (!_if_cond_eval(n, nn)) { return false; } } return mn->child != 0; } static bool _if_cond_eval(struct node *n, struct node *mn) { if (node_is_value(mn)) { return true; } const char *op = mn->value; if (!op) { node_fatal(AK_ERROR_SCRIPT_SYNTAX, n, "Matching condition is not set"); } bool eq = false; bool neg = false; if (mn->flags & NODE_FLG_NEGATE) { neg = true; } if (mn->type == NODE_TYPE_BAG) { if (*op == '!') { ++op; } if (strcmp(op, "eq") == 0) { eq = _if_matched_eval(mn); } else if (strcmp(op, "in") == 0) { eq = _if_in_eval(mn); } else if (strcmp(op, "defined") == 0) { eq = _if_defined_eval(mn); } else if (strcmp(op, "or") == 0) { eq = _if_OR_eval(n, mn); } else if (strcmp(op, "and") == 0) { eq = _if_AND_eval(n, mn); } else if (strcmp(op, "prefix") == 0) { eq = _if_prefix_eval(mn); } else if (strcmp(op, "contains") == 0) { eq = _if_contains_eval(mn); } else { node_fatal(AK_ERROR_SCRIPT_SYNTAX, n, "Unknown matching condition: %s", op); } } else if (node_is_can_be_value(mn)) { op = node_value(mn); if (is_vlist(op)) { struct vlist_iter iter; vlist_iter_init(op, &iter); if (vlist_iter_next(&iter) && !(iter.len == 1 && iter.item[0] == '0')) { eq = iter.len > 0 || vlist_iter_next(&iter); } } else { eq = (op && *op != '\0' && !(op[0] == '0' && op[1] == '\0')); } } else { node_fatal(AK_ERROR_SCRIPT_SYNTAX, n, "Unknown matching condition: %s", op); } if (neg) { eq = !eq; } if (g_env.verbose) { node_info(n, "Evaluated to: %s", (eq ? "true" : "false")); } return eq; } static inline bool _if_node_is_else(struct node *n) { return n && n->value && strcmp(n->value, "else") == 0; } static void _if_pull_else(struct node *n) { struct node *prev = node_find_prev_sibling(n); struct node *next = n->next; struct node *nn = next; bool elz = false; if (_if_node_is_else(nn)) { elz = true; next = nn->next; } n->child = 0; if (elz && nn->child) { nn = nn->child; n->next = nn; // Keep upper iterations (if any) be consistent. n->child = 0; if (prev) { prev->next = nn; } else { n->parent->child = nn; } for ( ; nn; nn = nn->next) { nn->parent = n->parent; if (nn->next == 0) { nn->next = next; break; } } } else if (prev) { n->next = next; prev->next = next; } else { n->next = next; n->parent->child = next; } } static void _if_pull_if(struct node *n) { struct node *mn = n->child; struct node *prev = node_find_prev_sibling(n); struct node *next = n->next; struct node *nn = mn->next; if (_if_node_is_else(next)) { next = next->next; // Skip else block } n->child = 0; if (nn) { n->next = nn; // Keep upper iterations (if any) be consistent. if (prev) { prev->next = nn; } else { n->parent->child = nn; } for ( ; nn; nn = nn->next) { nn->parent = n->parent; if (nn->next == 0) { nn->next = next; break; } } } else if (prev) { n->next = next; prev->next = next; } else { n->next = next; n->parent->child = next; } } static void _if_init(struct node *n) { struct node *cn = n->child; // Conditional node if (!cn) { node_fatal(AK_ERROR_SCRIPT_SYNTAX, n, "'if {...}' must have a condition clause"); } node_init(cn); // Explicitly init conditional node since in script init worflow 'if' initiated first. bool matched = _if_cond_eval(n, cn); if (matched) { _if_pull_if(n); } else { _if_pull_else(n); } n->init = 0; // Protect me from second call in any way } int node_if_setup(struct node *n) { n->init = _if_init; return 0; } #ifndef _AMALGAMATE_ #include "script.h" #include "xstr.h" #include "utils.h" #include #endif static const char* _join_value(struct node *n) { struct node_foreach *fe = node_find_parent_foreach(n); if (fe) { free(n->impl); n->impl = 0; } if (n->impl) { return n->impl; } struct xstr *xstr = xstr_create_empty(); int c = 0; struct node *pair[] = { 0, 0 }; for (struct node *nn = n->child; nn; nn = nn->next, ++c) { if (c < 2) { pair[c] = nn; } } if (c == 2) { const char *vpair[] = { node_value(pair[0]), node_value(pair[1]) }; if ((vpair[0] && !is_vlist(vpair[0]) && is_vlist(vpair[1]))) { const char *prefix = vpair[0]; size_t prefix_len = strlen(prefix); struct vlist_iter iter; vlist_iter_init(vpair[1], &iter); while (vlist_iter_next(&iter)) { xstr_cat2(xstr, "\1", 1); xstr_cat2(xstr, prefix, prefix_len); xstr_cat2(xstr, iter.item, iter.len); } n->impl = xstr_destroy_keep_ptr(xstr); return n->impl; } else if (vpair[1] && !is_vlist(vpair[1]) && is_vlist(vpair[0])) { const char *suffix = vpair[1]; size_t suffix_len = strlen(suffix); struct vlist_iter iter; vlist_iter_init(vpair[0], &iter); while (vlist_iter_next(&iter)) { xstr_cat2(xstr, "\1", 1); xstr_cat2(xstr, iter.item, iter.len); xstr_cat2(xstr, suffix, suffix_len); } n->impl = xstr_destroy_keep_ptr(xstr); return n->impl; } } bool list = n->value[0] == '.'; for (struct node *nn = n->child; nn; nn = nn->next) { if (list) { xstr_cat(xstr, "\1"); } const char *val = node_value(nn); if (is_vlist(val)) { struct vlist_iter iter; vlist_iter_init(val, &iter); while (vlist_iter_next(&iter)) { xstr_cat2(xstr, iter.item, iter.len); } } else { xstr_cat(xstr, val); } } n->impl = xstr_destroy_keep_ptr(xstr); return n->impl; } static void _join_dispose(struct node *n) { if (n->impl) { free(n->impl); } } int node_join_setup(struct node *n) { n->flags |= NODE_FLG_NO_CWD; n->value_get = _join_value; n->dispose = _join_dispose; return 0; } #ifndef _AMALGAMATE_ #include "script.h" #include "xstr.h" #include "utils.h" #include #endif static void _meta_on_let(struct node *n, struct node *e) { const char *name = node_value(e->child); if (name == 0) { return; } struct xstr *xstr = xstr_create_empty(); for (struct node *nn = e->child->next; nn; nn = nn->next) { if (nn != e->child->next) { xstr_cat2(xstr, " ", 1); } const char *nv = node_value(nn); if (is_vlist(nv)) { struct vlist_iter iter; vlist_iter_init(nv, &iter); for (int i = 0; vlist_iter_next(&iter); ++i) { if (i) { xstr_cat2(xstr, " ", 1); } xstr_cat2(xstr, iter.item, iter.len); } } else { xstr_cat(xstr, nv); } } node_env_set(n, name, xstr_ptr(xstr)); xstr_destroy(xstr); } static void _meta_on_entry(struct node *n, struct node *e) { if (e->type != NODE_TYPE_BAG) { return; } const int plen = AK_LLEN("META_"); char name[plen + strlen(e->value) + 1]; char *wp = name; memcpy(wp, "META_", plen), wp += plen; memcpy(wp, e->value, sizeof(name) - plen); for (int i = plen; i < sizeof(name) - 1; ++i) { name[i] = utils_toupper_ascii(name[i]); } struct xstr *xstr = xstr_create_empty(); for (struct node *nn = e->child; nn; nn = nn->next) { if (nn != e->child) { xstr_cat2(xstr, " ", 1); } const char *nv = node_value(nn); xstr_cat(xstr, nv); } node_env_set(n, name, xstr_ptr(xstr)); xstr_destroy(xstr); } static void _meta_init(struct node *n) { for (struct node *nn = n->child; nn; nn = nn->next) { if (strcmp(nn->value, "let") == 0) { _meta_on_let(n, nn); } else { _meta_on_entry(n, nn); } } } int node_meta_setup(struct node *n) { n->init = _meta_init; return 0; } #ifndef _AMALGAMATE_ #include "script.h" #include "log.h" #endif static void _include(struct node *n, struct node *cn) { const char *file = node_value(cn); if (!file || *file == '\0') { node_fatal(AK_ERROR_SCRIPT, n, "No Autark script specified"); } struct node *nn = 0; struct node *pn = node_find_prev_sibling(n); int rc = script_include(n->parent, file, &nn); if (rc) { node_fatal(rc, n, "Failed to open Autark script: %s", file); } akassert(nn); if (pn) { pn->next = nn; } else if (n->parent) { n->parent->child = nn; } nn->next = n->next; n->next = nn; // Keep upper iterations (if any) be consistent. n->child = 0; } static void _include_init(struct node *n) { struct node *cn = n->child; if (!cn) { node_fatal(AK_ERROR_SCRIPT, n, "Missing path in 'include {...}' directive"); } node_init(cn); // Explicitly init conditional node since in script init worflow 'include' initiated first. _include(n, cn); n->init = 0; } int node_include_setup(struct node *n) { n->init = _include_init; return 0; } #ifndef _AMALGAMATE_ #include "script.h" #include "spawn.h" #include "env.h" #include "log.h" #include "spawn.h" #include "utils.h" #include "log.h" #include "paths.h" #include #include #endif struct _run_spawn_data { struct node *n; const char *cmd; }; static void _run_stdout_handler(char *buf, size_t buflen, struct spawn *s) { fprintf(stdout, "%s", buf); } static void _run_stderr_handler(char *buf, size_t buflen, struct spawn *s) { fprintf(stderr, "%s", buf); } struct _run_on_resolve_ctx { struct node_resolve *r; struct node_foreach *fe; struct ulist consumes; // sizeof(char*) struct ulist consumes_foreach; // sizeof(char*) bool fe_consumed; // Foreach variable is consumed }; static void _run_on_resolve_shell(struct node_resolve *r, struct node *nn_) { struct node *n = r->n; struct xstr *xstr = xstr_create_empty(); for (struct node *nn = nn_; nn; nn = nn->next) { if (nn != nn_) { xstr_cat2(xstr, " ", 1); } const char *v = node_value(nn); if (is_vlist(v)) { struct vlist_iter iter; vlist_iter_init(v, &iter); while (vlist_iter_next(&iter)) { if (xstr_size(xstr)) { xstr_cat2(xstr, " ", 1); } xstr_cat2(xstr, iter.item, iter.len); } } else { xstr_cat(xstr, v); } } const char *shell = "/bin/sh"; struct unit *unit = unit_peek(); struct spawn *s = spawn_create(shell, &(struct _run_spawn_data) { .cmd = shell, .n = n }); spawn_env_path_prepend(s, unit->dir); spawn_env_path_prepend(s, unit->cache_dir); spawn_set_stdout_handler(s, _run_stdout_handler); spawn_set_stderr_handler(s, _run_stderr_handler); spawn_arg_add(s, "-c"); spawn_arg_add(s, xstr_ptr(xstr)); if (g_env.check.log) { xstr_printf(g_env.check.log, "%s: %s\n", n->name, shell); } int rc = spawn_do(s); if (rc) { node_fatal(rc, n, "%s", shell); } else { int code = spawn_exit_code(s); if (code != 0) { node_fatal(AK_ERROR_EXTERNAL_COMMAND, n, "%s: %d", shell, code); } } spawn_destroy(s); xstr_destroy(xstr); } static void _run_on_resolve_exec(struct node_resolve *r, struct node *ncmd) { struct node *n = r->n; const char *cmd = node_value(ncmd); if (!cmd) { node_fatal(AK_ERROR_FAIL, n, "No run command specified"); } struct unit *unit = unit_peek(); struct spawn *s = spawn_create(cmd, &(struct _run_spawn_data) { .cmd = cmd, .n = n }); spawn_env_path_prepend(s, unit->dir); spawn_env_path_prepend(s, unit->cache_dir); spawn_set_stdout_handler(s, _run_stdout_handler); spawn_set_stderr_handler(s, _run_stderr_handler); for (struct node *nn = ncmd->next; nn; nn = nn->next) { if (node_is_can_be_value(nn)) { spawn_arg_add(s, node_value(nn)); } } if (g_env.check.log) { xstr_printf(g_env.check.log, "%s: %s\n", n->name, cmd); } int rc = spawn_do(s); if (rc) { node_fatal(rc, n, "%s", cmd); } else { int code = spawn_exit_code(s); if (code != 0) { node_fatal(AK_ERROR_EXTERNAL_COMMAND, n, "%s: %d", cmd, code); } } spawn_destroy(s); } static void _run_on_resolve_do(struct node_resolve *r, struct node *n) { for (struct node *nn = n->child; nn; nn = nn->next) { if (strcmp(nn->value, "exec") == 0) { _run_on_resolve_exec(r, nn->child); } else if (strcmp(nn->value, "shell") == 0) { _run_on_resolve_shell(r, nn->child); } } } static void _run_on_resolve(struct node_resolve *r) { struct node *n = r->n; struct _run_on_resolve_ctx *ctx = r->user_data; if (ctx->fe) { if (ctx->fe_consumed) { // Foreach variable is consumed by run struct ulist flist_ = { .usize = sizeof(char*) }; struct ulist *flist = &ctx->consumes_foreach; if (r->resolve_outdated.num) { for (int i = 0; i < r->resolve_outdated.num; ++i) { struct resolve_outdated *u = ulist_get(&r->resolve_outdated, i); if (u->flags != 'f') { flist = &ctx->consumes_foreach; // Fallback to all consumed break; } char *path = pool_strdup(r->pool, u->path); ulist_push(&flist_, &path); flist = &flist_; } } struct unit *unit = unit_peek(); for (int i = 0; i < flist->num; ++i) { char *p, *fi = *(char**) ulist_get(flist, i); if (n->flags & NODE_FLG_IN_CACHE) { p = path_relativize_cwd(unit->cache_dir, fi, unit->cache_dir); } else if (n->flags & NODE_FLG_IN_SRC) { p = path_relativize_cwd(unit->dir, fi, unit->dir); } else { p = fi; } ctx->fe->value = p; _run_on_resolve_do(r, n); ctx->fe->value = 0; if (p != fi) { free(p); } } ulist_destroy_keep(&flist_); } else { struct vlist_iter iter; vlist_iter_init(ctx->fe->items, &iter); while (vlist_iter_next(&iter)) { char buf[iter.len + 1]; memcpy(buf, iter.item, iter.len); buf[iter.len] = '\0'; ctx->fe->value = buf; _run_on_resolve_do(r, n); ctx->fe->value = 0; } } } else { _run_on_resolve_do(r, n); } struct deps deps; int rc = deps_open(r->deps_path_tmp, 0, &deps); if (rc) { node_fatal(rc, n, "Failed to open dependency file: %s", r->deps_path_tmp); } node_add_unit_deps(n, &deps); for (int i = 0; i < r->node_val_deps.num; ++i) { struct node *nv = *(struct node**) ulist_get(&r->node_val_deps, i); const char *val = node_value(nv); if (val) { deps_add(&deps, DEPS_TYPE_NODE_VALUE, 0, val, i); } } for (int i = 0; i < ctx->consumes.num; ++i) { const char *path = *(const char**) ulist_get(&ctx->consumes, i); deps_add(&deps, DEPS_TYPE_FILE, 0, path, 0); } for (int i = 0; i < ctx->consumes_foreach.num; ++i) { const char *path = *(const char**) ulist_get(&ctx->consumes_foreach, i); deps_add(&deps, DEPS_TYPE_FILE, 'f', path, 0); } node_products_add_as_deps(n, &deps); deps_close(&deps); } static bool _run_setup_foreach(struct node *n) { struct node_foreach *fe = node_find_parent_foreach(n); if (!fe) { return false; } struct node *pn = node_find_direct_child(n, NODE_TYPE_BAG, "produces"); if (pn && pn->child) { struct vlist_iter iter; vlist_iter_init(fe->items, &iter); while (vlist_iter_next(&iter)) { char buf[iter.len + 1]; memcpy(buf, iter.item, iter.len); buf[iter.len] = '\0'; fe->value = buf; for (struct node *nn = pn->child; nn; nn = nn->next) { if (node_is_can_be_value(nn)) { const char *value = node_value(nn); if (g_env.verbose) { node_info(n, "Product: %s", value); } node_product_add(n, value, 0); } } fe->value = 0; } } return true; } static void _run_setup(struct node *n) { if (_run_setup_foreach(n)) { return; } struct node *nn = node_find_direct_child(n, NODE_TYPE_BAG, "produces"); if (nn && nn->child) { for (nn = nn->child; nn; nn = nn->next) { if (node_is_can_be_value(nn)) { const char *value = node_value(nn); if (g_env.verbose) { node_info(n, "Product: %s", value); } node_product_add(n, value, 0); } } } } static void _run_on_consumed_resolved(const char *path_, void *d) { struct _run_on_resolve_ctx *ctx = d; const char *path = pool_strdup(ctx->r->pool, path_); ulist_push(&ctx->consumes, &path); } static void _run_on_consumed_resolved_foreach(const char *path_, void *d) { struct _run_on_resolve_ctx *ctx = d; const char *path = pool_strdup(ctx->r->pool, path_); ulist_push(&ctx->consumes_foreach, &path); } static void _run_on_resolve_init(struct node_resolve *r) { struct _run_on_resolve_ctx *ctx = r->user_data; ctx->r = r; struct node *nn = node_find_direct_child(r->n, NODE_TYPE_BAG, "consumes"); if (ctx->fe) { ctx->fe->value = 0; ctx->fe->access_cnt = 0; } if (nn && nn->child) { node_consumes_resolve(r->n, nn->child, 0, _run_on_consumed_resolved, ctx); } if (ctx->fe && ctx->fe->access_cnt > 0) { ctx->fe_consumed = true; struct vlist_iter iter; struct ulist paths = { .usize = sizeof(char*) }; vlist_iter_init(ctx->fe->items, &iter); while (vlist_iter_next(&iter)) { char buf[iter.len + 1]; memcpy(buf, iter.item, iter.len); char *p = pool_strndup(r->pool, buf, iter.len); ulist_push(&paths, &p); } node_consumes_resolve(r->n, 0, &paths, _run_on_consumed_resolved_foreach, ctx); ulist_destroy_keep(&paths); } } static void _run_build(struct node *n) { struct _run_on_resolve_ctx ctx = { .consumes = { .usize = sizeof(char*) }, .consumes_foreach = { .usize = sizeof(char*) }, .fe = node_find_parent_foreach(n), }; struct node_resolve r = { .n = n, .path = n->vfile, .user_data = &ctx, .on_init = _run_on_resolve_init, .on_resolve = _run_on_resolve, .node_val_deps = { .usize = sizeof(struct node*) }, }; r.force_outdated = n->post_build != 0 || node_find_direct_child(n, NODE_TYPE_VALUE, "always") != 0; for (struct node *nn = n->child; nn; nn = nn->next) { if (strcmp(nn->value, "exec") == 0 || strcmp(nn->value, "shell") == 0) { for (struct node *cn = nn->child; cn; cn = cn->next) { if (node_is_value_may_be_dep_saved(cn, 0)) { ulist_push(&r.node_val_deps, &cn); } } } } node_resolve(&r); ulist_destroy_keep(&ctx.consumes); ulist_destroy_keep(&ctx.consumes_foreach); } int node_run_setup(struct node *n) { if (strcmp("run-on-install", n->value) == 0) { if (g_env.install.enabled) { n->flags |= NODE_FLG_IN_CACHE; n->setup = _run_setup; n->post_build = _run_build; } } else { n->flags |= NODE_FLG_IN_CACHE; n->setup = _run_setup; n->build = _run_build; } return 0; } #ifndef _AMALGAMATE_ #include "script.h" #include "env.h" #include "pool.h" #include "paths.h" #include "utils.h" #include "xstr.h" #include "ulist.h" #include "alloc.h" #include "utils.h" #include #include #include #include #endif struct _configure_ctx { struct pool *pool; struct node *n; struct ulist sources; // char* }; static char* _line_replace_def(struct node *n, struct deps *deps, char *line) { char key[4096]; const char *ep = line + AK_LLEN("//autarkdef "); while (utils_char_is_space(*ep)) ++ep; const char *sp = ep; while (*ep != '\0' && !utils_char_is_space(*ep)) ++ep; const char *np = ep; while (*np != '\0' && utils_char_is_space(*np)) ++np; if (*np == '\"') { utils_strnncpy(key, sp, ep - sp, sizeof(key)); const char *val = node_env_get(n, key); deps_add_env(deps, 0, key, val ? val : ""); if (val) { struct xstr *xstr = xstr_create_empty(); xstr_printf(xstr, "#define %s \"%s\"\n", key, val); return xstr_destroy_keep_ptr(xstr); } } else if (*np >= '0' && *np <= '9') { utils_strnncpy(key, sp, ep - sp, sizeof(key)); const char *val = node_env_get(n, key); deps_add_env(deps, 0, key, val ? val : ""); if (val) { struct xstr *xstr = xstr_create_empty(); xstr_printf(xstr, "#define %s %s", key, np); return xstr_destroy_keep_ptr(xstr); } else { struct xstr *xstr = xstr_create_empty(); xstr_printf(xstr, "#define %s 0\n", key); return xstr_destroy_keep_ptr(xstr); } } else { utils_strnncpy(key, sp, ep - sp, sizeof(key)); const char *val = node_env_get(n, key); deps_add_env(deps, 0, key, val ? val : ""); if (val) { struct xstr *xstr = xstr_create_empty(); xstr_printf(xstr, "#define %s\n", key); return xstr_destroy_keep_ptr(xstr); } } return line; } static char* _line_replace_subs(struct node *n, struct deps *deps, char *line) { char *s = line; char *sp = strchr(line, '@'); if (!sp) { return line; } struct xstr *xstr = xstr_create_empty(); struct xstr *kstr = xstr_create_empty(); do { xstr_cat2(xstr, s, sp - s); xstr_clear(kstr); char *p = ++sp; sp = 0; while (*p) { if (*p == '@') { const char *key = xstr_ptr(kstr); const char *val = node_env_get(n, key); deps_add_env(deps, 0, key, val ? val : ""); if (val && *val != '\0') { if (is_vlist(val)) { ++val; size_t len = strlen(val); char buf[len + 1]; memcpy(buf, val, len + 1); utils_chars_replace(buf, '\1', ' '); xstr_cat(xstr, buf); } else { xstr_cat(xstr, val); } } s = ++p; sp = strchr(s, '@'); xstr_clear(kstr); break; } else { xstr_cat2(kstr, p++, 1); } } } while (sp); if (xstr_size(kstr)) { xstr_cat2(xstr, "@", 1); xstr_cat(xstr, xstr_ptr(kstr)); } else { xstr_cat(xstr, s); } xstr_destroy(kstr); return xstr_destroy_keep_ptr(xstr); } static void _process_file(struct node *n, const char *src, const char *tgt, struct deps *deps) { struct xstr *xstr = 0; if (strcmp(src, tgt) == 0) { xstr = xstr_create_empty(); xstr_printf(xstr, "%s.tmp", src); tgt = xstr_ptr(xstr); } path_mkdirs_for(tgt); FILE *f = fopen(src, "r"); if (!f) { node_fatal(errno, n, "Failed to open file: %s", src); } FILE *t = fopen(tgt, "w"); if (!t) { node_fatal(errno, n, "Failed to open file for writing: %s", tgt); } char *line; char buf[16384]; while ((line = fgets(buf, sizeof(buf), f))) { char *rl; if (utils_startswith(line, "//autarkdef ")) { rl = _line_replace_def(n, deps, line); } else { rl = _line_replace_subs(n, deps, line); } if (rl && fputs(rl, t) == EOF) { node_fatal(AK_ERROR_IO, n, "File write fail: %s", tgt); } if (rl != line) { free(rl); } } // Transfer file permissions struct stat st; if (stat(src, &st) == -1) { node_fatal(errno, n, "Failed to stat source file: %s", src); } if (fchmod(fileno(t), st.st_mode & 07777) == -1) { node_fatal(errno, n, "Failed to set permissions on target file: %s", tgt); } fclose(f); fclose(t); if (xstr) { int rc = utils_rename_file(tgt, src); if (rc) { node_fatal(rc, n, "Rename failed of %s to %s", tgt, src); } xstr_destroy(xstr); } } static void _configure_on_resolve(struct node_resolve *r) { struct node *n = r->n; struct _configure_ctx *ctx = n->impl; struct ulist *slist = &ctx->sources; struct ulist rlist = { .usize = sizeof(char*) }; struct unit *unit = unit_peek(); struct deps deps; int rc = deps_open(r->deps_path_tmp, 0, &deps); if (rc) { node_fatal(rc, n, "Failed to open dependency file: %s", r->deps_path_tmp); } node_add_unit_deps(n, &deps); if (r->resolve_outdated.num) { for (int i = 0; i < r->resolve_outdated.num; ++i) { struct resolve_outdated *u = ulist_get(&r->resolve_outdated, i); if (u->flags != 's') { slist = &ctx->sources; break; } else { char *path = pool_strdup(r->pool, u->path); ulist_push(&rlist, &path); slist = &rlist; } } } for (int i = 0; i < slist->num; ++i) { char buf[PATH_MAX]; char *tgt, *src = *(char**) ulist_get(slist, i); src = path_normalize_cwd(src, unit->cache_dir, buf); bool incache = path_is_prefix_for(g_env.project.cache_dir, src, unit->cache_dir); if (!incache) { tgt = path_relativize_cwd(unit->dir, src, unit->dir); src = path_relativize_cwd(unit->cache_dir, src, unit->cache_dir); } else { tgt = path_relativize_cwd(unit->cache_dir, src, unit->cache_dir); src = xstrdup(tgt); } if (utils_endswith(tgt, ".in")) { size_t len = strlen(tgt); tgt[len - AK_LLEN(".in")] = '\0'; } _process_file(n, src, tgt, &deps); free(tgt); free(src); } for (int i = 0; i < ctx->sources.num; ++i) { char *src = *(char**) ulist_get(&ctx->sources, i); deps_add(&deps, DEPS_TYPE_FILE, 's', src, 0); } node_products_add_as_deps(n, &deps); deps_close(&deps); ulist_destroy_keep(&rlist); } static void _configure_on_resolve_init(struct node_resolve *r) { struct node *n = r->n; struct _configure_ctx *ctx = n->impl; node_consumes_resolve(n, 0, &ctx->sources, 0, 0); } static void _configure_build(struct node *n) { struct node_resolve r = { .n = n, .path = n->vfile, .on_init = _configure_on_resolve_init, .on_resolve = _configure_on_resolve, }; node_resolve(&r); } static void _configure_init(struct node *n) { struct pool *pool = pool_create_empty(); struct _configure_ctx *ctx = n->impl = pool_calloc(pool, sizeof(*ctx)); ctx->pool = pool; ulist_init(&ctx->sources, 32, sizeof(char*)); } static void _setup_item(struct node *n, const char *v) { char buf[PATH_MAX]; struct _configure_ctx *ctx = n->impl; struct unit *unit = unit_peek(); char *tgt = 0, *src = path_normalize_cwd(v, unit->dir, buf); bool incache = path_is_prefix_for(g_env.project.cache_dir, src, unit->cache_dir); if (!incache) { tgt = path_relativize_cwd(unit->dir, src, unit->dir); src = path_relativize_cwd(unit->cache_dir, src, unit->cache_dir); } else { tgt = path_relativize_cwd(unit->cache_dir, src, unit->cache_dir); src = xstrdup(tgt); } if (utils_endswith(tgt, ".in")) { size_t len = strlen(tgt); tgt[len - AK_LLEN(".in")] = '\0'; } node_product_add(n, tgt, 0); v = pool_strdup(ctx->pool, src); ulist_push(&ctx->sources, &v); free(tgt); free(src); } static void _configure_setup(struct node *n) { for (struct node *nn = n->child; nn; nn = nn->next) { const char *v = node_value(nn); if (is_vlist(v)) { struct vlist_iter iter; while (vlist_iter_next(&iter)) { char buf[iter.len + 1]; memcpy(buf, iter.item, iter.len); buf[iter.len] = '\0'; _setup_item(n, buf); } } else { _setup_item(n, v); } } } static void _configure_dispose(struct node *n) { struct _configure_ctx *ctx = n->impl; if (ctx) { ulist_destroy_keep(&ctx->sources); pool_destroy(ctx->pool); } } int node_configure_setup(struct node *n) { n->flags |= NODE_FLG_IN_CACHE; n->dispose = _configure_dispose; n->init = _configure_init; n->setup = _configure_setup; n->build = _configure_build; return 0; } #ifndef _AMALGAMATE_ #include "script.h" #include "ulist.h" #include "pool.h" #include "log.h" #include "paths.h" #include "env.h" #include "utils.h" #include "xstr.h" #include "spawn.h" #include "alloc.h" #include "map.h" #include #include #include #include #include #endif /// Compilation database state. struct _cdb { const char *path; char _path[PATH_MAX]; } _cdb; struct _cc_ctx { struct pool *pool; struct ulist sources; // char* struct ulist objects; // char* struct node *n; struct node *n_cflags; struct node *n_sources; struct node *n_cc; struct node *n_consumes; struct node *n_objects; const char *cc; const char *objskey; struct ulist consumes; // sizeof(char*) int num_failed; }; static void _cc_cdb_entry_add(struct node *n, struct spawn *s, const char *src, const char *tgt); static void _cc_deps_MMD_item_add(const char *item, struct node *n, struct deps *deps, const char *src) { char buf[127]; char *p = strrchr(item, '.'); if (!p || p[1] == '\0') { return; } ++p; char *ext = utils_strncpy(buf, p, sizeof(buf)); if (*ext == 'c' || *ext == 'C' || *ext == 'm') { // Skip source files return; } deps_add_alias(deps, 's', src, item); } static void _cc_deps_MMD_add(struct node *n, struct deps *deps, const char *src, const char *obj) { char buf[MAX(2 * PATH_MAX, 8192)]; size_t len = strlen(obj); utils_strncpy(buf, obj, sizeof(buf)); char *p = strrchr(buf, '.'); akassert(p && p[1] != '\0'); p[1] = 'd'; p[2] = '\0'; FILE *f = fopen(buf, "r"); if (!f) { node_warn(n, "Failed to open compiler generated (-MMD) dependency file: %s", buf); return; } bool nl = false; while ((p = fgets(buf, sizeof(buf), f))) { if (!nl) { if (!utils_startswith(p, obj)) { continue; } p += len; if (*p++ != ':') { continue; } } else { nl = false; } while (*p != '\0') { while (*p != '\0' && utils_char_is_space(*p)) { ++p; } if (p[0] == '\\' && p[1] == '\n') { nl = true; break; } char *sp = p; char *ep = sp; while (*p != '\0' && !utils_char_is_space(*p)) { ep = p; ++p; } if (ep > sp) { if (*p != '\0') { *p = '\0'; ++p; } _cc_deps_MMD_item_add(sp, n, deps, src); } } } fclose(f); } struct _cc_task { struct spawn *s; char *src; char *obj; pid_t pid; }; static void _cc_task_destroy(struct _cc_task *t) { spawn_destroy(t->s); free(t->src); free(t->obj); } static void _cc_on_build_source( struct node *n, struct deps *deps, const char *src, const char *obj, struct _cc_task *task) { if (g_env.check.log) { xstr_printf(g_env.check.log, "%s: build src=%s obj=%s\n", n->name, src, obj); } struct _cc_ctx *ctx = n->impl; struct spawn *s = spawn_create(ctx->cc, ctx); task->s = s; spawn_set_nowait(s, true); if (ctx->n_cflags) { struct xstr *xstr = 0; const char *cflags = node_value(ctx->n_cflags); if (!is_vlist(cflags)) { xstr = xstr_create_empty(); utils_split_values_add(cflags, xstr); cflags = xstr_ptr(xstr); } spawn_arg_add(s, cflags); xstr_destroy(xstr); } if (!spawn_arg_starts_with(s, "-M")) { spawn_arg_add(s, "-MMD"); } spawn_arg_add(s, "-I./"); // Current cache dir spawn_arg_add(s, "-c"); spawn_arg_add(s, src); spawn_arg_add(s, "-o"); spawn_arg_add(s, obj); _cc_cdb_entry_add(n, s, src, obj); int rc = spawn_do(s); if (rc) { spawn_destroy(s); node_error(rc, ctx->n, "%s", ctx->cc); return; } task->pid = spawn_pid(s); } static void _cc_on_resolve(struct node_resolve *r) { struct deps deps; struct _cc_ctx *ctx = r->user_data; struct unit *unit = unit_peek(); struct ulist *slist = &ctx->sources; struct ulist rlist = { .usize = sizeof(char*) }; struct ulist tasks = { .usize = sizeof(struct _cc_task) }; struct map *fmap = map_create_str(map_k_free); int max_jobs = g_env.max_parallel_jobs; if (max_jobs <= 0) { max_jobs = 1; } if (r->resolve_outdated.num) { for (int i = 0; i < r->resolve_outdated.num; ++i) { struct resolve_outdated *u = ulist_get(&r->resolve_outdated, i); if (u->flags != 's') { // Rebuild all on any outdated non source dependency slist = &ctx->sources; break; } else { char *path = pool_strdup(r->pool, u->path); ulist_push(&rlist, &path); slist = &rlist; } } } int rc = deps_open(r->deps_path_tmp, 0, &deps); if (rc) { node_fatal(rc, ctx->n, "Failed to open dependency file: %s", r->deps_path_tmp); } node_add_unit_deps(ctx->n, &deps); for (int i = 0; i < r->node_val_deps.num; ++i) { struct node *nv = *(struct node**) ulist_get(&r->node_val_deps, i); const char *val = node_value(nv); if (val) { deps_add(&deps, DEPS_TYPE_NODE_VALUE, 0, val, i); } } for (int i = 0; i < ctx->consumes.num; ++i) { const char *path = *(const char**) ulist_get(&ctx->consumes, i); deps_add(&deps, DEPS_TYPE_FILE, 0, path, 0); } for (int i = 0; i < slist->num || tasks.num > 0; ) { while (tasks.num < max_jobs && i < slist->num) { char buf[PATH_MAX]; char *obj, *src = *(char**) ulist_get(slist, i); src = path_normalize_cwd(src, unit->cache_dir, buf); bool incache = path_is_prefix_for(g_env.project.cache_dir, src, unit->cache_dir); if (!incache) { obj = path_relativize_cwd(unit->dir, src, unit->dir); src = path_relativize_cwd(unit->cache_dir, src, unit->cache_dir); } else { obj = path_relativize_cwd(unit->cache_dir, src, unit->cache_dir); src = xstrdup(obj); } char *p = strrchr(obj, '.'); akassert(p && p[1] != '\0'); p[1] = 'o'; p[2] = '\0'; struct _cc_task task = { .src = src, .obj = obj, .pid = -1 }; _cc_on_build_source(ctx->n, &deps, src, obj, &task); if (task.pid == -1) { ++ctx->num_failed; map_put_str(fmap, src, (void*) (intptr_t) 1); if (slist == &ctx->sources) { deps_add(&deps, DEPS_TYPE_FILE_OUTDATED, 's', src, 0); } _cc_task_destroy(&task); } else { ulist_push(&tasks, &task); } ++i; } int wstatus = 0; pid_t pid = wait(&wstatus); if (pid == -1) { akfatal(errno, "wait() syscall failed", 0); } for (int j = 0; j < tasks.num; ++j) { struct _cc_task *t = (struct _cc_task*) ulist_get(&tasks, j); if (t->pid == pid) { spawn_set_wstatus(t->s, wstatus); int code = spawn_exit_code(t->s); if (code != 0) { ++ctx->num_failed; map_put_str(fmap, t->src, (void*) (intptr_t) 1); } if (slist == &ctx->sources) { deps_add(&deps, code != 0 ? DEPS_TYPE_FILE_OUTDATED : DEPS_TYPE_FILE, 's', t->src, 0); if (code == 0) { _cc_deps_MMD_add(ctx->n, &deps, t->src, t->obj); } } _cc_task_destroy(t); ulist_remove(&tasks, j); break; } } } if (slist != &ctx->sources) { for (int i = 0; i < ctx->sources.num; ++i) { char buf[PATH_MAX]; char *obj, *src = *(char**) ulist_get(&ctx->sources, i); src = path_normalize_cwd(src, unit->cache_dir, buf); bool incache = path_is_prefix_for(g_env.project.cache_dir, src, unit->cache_dir); if (!incache) { obj = path_relativize_cwd(unit->dir, src, unit->dir); src = path_relativize_cwd(unit->cache_dir, src, unit->cache_dir); } else { obj = path_relativize_cwd(unit->cache_dir, src, unit->cache_dir); src = xstrdup(obj); } char *p = strrchr(obj, '.'); akassert(p && p[1] != '\0'); p[1] = 'o'; p[2] = '\0'; bool failed = map_get(fmap, src) != 0; deps_add(&deps, failed ? DEPS_TYPE_FILE_OUTDATED : DEPS_TYPE_FILE, 's', src, 0); if (!failed) { _cc_deps_MMD_add(ctx->n, &deps, src, obj); } free(obj); free(src); } } deps_close(&deps); ulist_destroy_keep(&rlist); ulist_destroy_keep(&tasks); map_destroy(fmap); } static void _cc_on_consumed_resolved(const char *path_, void *d) { struct node *n = d; struct _cc_ctx *ctx = n->impl; const char *path = pool_strdup(ctx->pool, path_); ulist_push(&ctx->consumes, &path); } static void _cc_on_resolve_init(struct node_resolve *r) { struct _cc_ctx *ctx = r->n->impl; if (ctx->n_consumes) { node_consumes_resolve(r->n, ctx->n_consumes->child, 0, _cc_on_consumed_resolved, r->n); } } static void _cc_cdb_init(struct node *n) { if (!g_env.project.compile_commands) { return; } const char *path = getenv(AUTARK_COMPILE_COMMANDS_ENV); if (!path) { return; } if (g_env.project.compile_commands_own) { unlink(path); } utils_strncpy(_cdb._path, path, sizeof(_cdb._path)); _cdb.path = _cdb._path; } struct _cc_cdb_arg_ctx { FILE *file; struct xstr *xstr; }; static void _cc_cdb_arg_add(int num, const char *arg, void *d) { struct _cc_cdb_arg_ctx *ctx = d; xstr_clear(ctx->xstr); utils_json_escape_str(arg, -1, ctx->xstr); if (num) { fprintf(ctx->file, ",\n %s", xstr_ptr(ctx->xstr)); } else { fprintf(ctx->file, "\n %s", xstr_ptr(ctx->xstr)); } } static void _cc_cdb_entry_add(struct node *n, struct spawn *s, const char *src, const char *tgt) { if (!_cdb.path) { return; } FILE *f = fopen(_cdb.path, "a"); if (!f) { node_fatal(errno, n, "Error opening file for writing: %s", _cdb.path); } long int pos = ftell(f); if (pos == -1) { node_fatal(errno, n, "File: %s", _cdb.path); } struct xstr *xstr = xstr_create_empty(); if (pos > 0) { fprintf(f, ",\n {\n"); } else { fprintf(f, "\n {\n"); } fprintf(f, " \"arguments\": ["); spawn_visit_cmd(s, &(struct _cc_cdb_arg_ctx) { f, xstr }, _cc_cdb_arg_add); fprintf(f, "\n ],\n"); struct unit *unit = unit_peek(); xstr_clear(xstr); utils_json_escape_str(unit->cache_dir, -1, xstr); fprintf(f, " \"directory\": %s,\n", xstr_ptr(xstr)); xstr_clear(xstr); utils_json_escape_str(src, -1, xstr); fprintf(f, " \"file\": %s,\n", xstr_ptr(xstr)); xstr_clear(xstr); utils_json_escape_str(tgt, -1, xstr); fprintf(f, " \"output\": %s\n", xstr_ptr(xstr)); fprintf(f, " }"); xstr_destroy(xstr); fclose(f); } static void _cc_post_build(struct node *n) { int rc = 0; if (!_cdb.path || !g_env.project.compile_commands_own) { return; } const char *path = _cdb.path; _cdb.path = 0; FILE *sf = fopen(path, "r"); if (!sf) { return; } unlink(path); path = _cdb._path; snprintf(_cdb._path, sizeof(_cdb._path), "%s/compile_commands.json", g_env.project.cache_dir); FILE *tf = fopen(path, "w"); if (!tf) { rc = errno; fclose(sf); node_fatal(rc, n, "Error opening file for writing: %s", path); } fputs("[", tf); rc = utils_copy_file_streams(sf, tf); if (rc) { fclose(sf); fclose(tf); node_fatal(rc, n, "Error writing file: %s", path); } fputs("\n]", tf); fclose(sf); fclose(tf); node_info(n, "Compilation database file created: %s", path); } static void _cc_build(struct node *n) { struct _cc_ctx *ctx = n->impl; char *objs = ulist_to_vlist(&ctx->objects); node_env_set(n, ctx->objskey, objs); free(objs); for (int i = 0; i < ctx->sources.num; ++i) { const char *src = *(char**) ulist_get(&ctx->sources, i); if (!path_is_exist(src)) { struct node *pn = node_by_product_raw(n, src); if (pn) { node_build(pn); } else { node_fatal(AK_ERROR_DEPENDENCY_UNRESOLVED, n, "'%s'", src); } } } struct node_resolve r = { .n = n, .path = n->vfile, .user_data = ctx, .on_init = _cc_on_resolve_init, .on_resolve = _cc_on_resolve, .node_val_deps = { .usize = sizeof(struct node*) } }; if (node_is_value_may_be_dep_saved(ctx->n_cc, NODE_TYPE_VALUE)) { ulist_push(&r.node_val_deps, &ctx->n_cc); } if (node_is_value_may_be_dep_saved(ctx->n_cflags, NODE_TYPE_VALUE)) { ulist_push(&r.node_val_deps, &ctx->n_cflags); } if (node_is_value_may_be_dep_saved(ctx->n_sources, NODE_TYPE_VALUE)) { ulist_push(&r.node_val_deps, &ctx->n_sources); } node_resolve(&r); if (ctx->num_failed) { akfatal2("Compilation terminated with errors!"); } } static void _cc_source_add(struct node *n, const char *src) { char buf[PATH_MAX], *p; if (src == 0 || *src == '\0') { return; } p = strrchr(src, '.'); if (p == 0 || p[1] == '\0') { return; } struct _cc_ctx *ctx = n->impl; struct unit *unit = unit_peek(); const char *npath = src; if (!path_is_absolute(src)) { npath = path_normalize_cwd(src, unit->dir, buf); } p = pool_strdup(ctx->pool, npath); ulist_push(&ctx->sources, &p); bool incache = path_is_prefix_for(g_env.project.cache_dir, p, unit->dir); const char *dir = incache ? unit->cache_dir : unit->dir; char *obj = path_relativize_cwd(dir, p, dir); p = strrchr(obj, '.'); akassert(p && p[1] != '\0'); p[1] = 'o'; p[2] = '\0'; ulist_push(&ctx->objects, &obj); node_product_add(n, obj, 0); } static void _cc_setup(struct node *n) { _cc_cdb_init(n); struct _cc_ctx *ctx = n->impl; const char *val = node_value(ctx->n_sources); if (is_vlist(val)) { struct vlist_iter iter; vlist_iter_init(val, &iter); while (vlist_iter_next(&iter)) { char buf[PATH_MAX]; utils_strnncpy(buf, iter.item, iter.len, sizeof(buf)); _cc_source_add(n, buf); } } else { _cc_source_add(n, val); } if (ctx->n_cc) { const char *cc = node_value(ctx->n_cc); if (cc && *cc != '\0') { ctx->cc = pool_strdup(ctx->pool, cc); } } if (!ctx->cc) { const char *key = strcmp(n->value, "cc") == 0 ? "CC" : "CXX"; if (key) { ctx->cc = pool_strdup(ctx->pool, node_env_get(n, key)); if (g_env.verbose && ctx->cc) { node_info(n, "Found '%s' compiler in ${%s}", ctx->cc, key); } } } if (!ctx->cc) { if (strcmp(n->value, "cc") == 0) { ctx->cc = "cc"; } else { ctx->cc = "c++"; } node_warn(n, "Fallback compiler: %s", ctx->cc); } else if (g_env.verbose) { node_info(n, "Compiler: %s", ctx->cc); } const char *objskey = 0; if (ctx->n_objects && ctx->n_objects->child) { objskey = node_value(ctx->n_objects->child); } if (!objskey) { if (strcmp(n->value, "cc") == 0) { objskey = "CC_OBJS"; } else { objskey = "CXX_OBJS"; } } if (g_env.verbose) { node_info(n, "Objects in ${%s}", objskey); } ctx->objskey = pool_strdup(ctx->pool, objskey); char *objs = ulist_to_vlist(&ctx->objects); node_env_set(n, ctx->objskey, objs); free(objs); } static void _cc_init(struct node *n) { struct _cc_ctx *ctx = n->impl; for (struct node *nn = n->child; nn; nn = nn->next) { if (strcmp(nn->value, "consumes") == 0) { ctx->n_consumes = nn; continue; } else if (strcmp(nn->value, "objects") == 0) { ctx->n_objects = nn; continue; } if (!ctx->n_sources) { ctx->n_sources = nn; continue; } if (!ctx->n_cflags) { ctx->n_cflags = nn; continue; } if (!ctx->n_cc) { ctx->n_cc = nn; } } if (!ctx->n_sources) { node_fatal(AK_ERROR_SCRIPT, n, "No sources specified"); } } static void _cc_dispose(struct node *n) { struct _cc_ctx *ctx = n->impl; if (ctx) { for (int i = 0; i < ctx->objects.num; ++i) { char *p = *(char**) ulist_get(&ctx->objects, i); free(p); } ulist_destroy_keep(&ctx->sources); ulist_destroy_keep(&ctx->objects); ulist_destroy_keep(&ctx->consumes); pool_destroy(ctx->pool); } } int node_cc_setup(struct node *n) { n->flags |= NODE_FLG_IN_CACHE; n->init = _cc_init; n->setup = _cc_setup; n->build = _cc_build; n->post_build = _cc_post_build; n->dispose = _cc_dispose; struct pool *pool = pool_create_empty(); struct _cc_ctx *ctx = pool_alloc(pool, sizeof(*ctx)); *ctx = (struct _cc_ctx) { .pool = pool, .n = n, .sources = { .usize = sizeof(char*) }, .objects = { .usize = sizeof(char*) }, .consumes = { .usize = sizeof(char*) }, }; n->impl = ctx; return 0; } #ifndef _AMALGAMATE_ #include "script.h" #include "log.h" #include "xstr.h" #include #include #endif static const char* _basename_value(struct node *n) { struct node_foreach *fe = node_find_parent_foreach(n); if (fe) { free(n->impl); n->impl = 0; } if (n->impl) { return n->impl; } const char *val = 0; const char *replace_ext = 0; if (!n->child || !(val = node_value(n->child))) { node_fatal(AK_ERROR_SCRIPT_SYNTAX, n, "Argument is not set"); } if (n->child->next) { replace_ext = node_value(n->child); } struct xstr *xstr = xstr_create_empty(); const char *dp = strrchr(val, '.'); if (dp) { xstr_cat2(xstr, val, (dp - val)); } else { xstr_cat(xstr, val); } if (replace_ext) { xstr_cat(xstr, replace_ext); } n->impl = xstr_destroy_keep_ptr(xstr); return n->impl; } static void _basename_dispose(struct node *n) { free(n->impl); } int node_basename_setup(struct node *n) { n->flags |= NODE_FLG_NO_CWD; n->value_get = _basename_value; n->dispose = _basename_dispose; return 0; } #ifndef _AMALGAMATE_ #include "script.h" #include "log.h" #include "alloc.h" #include "utils.h" #endif static void _foreach_setup(struct node *n) { if (!n->child) { node_fatal(AK_ERROR_SCRIPT_SYNTAX, n, "Foreach must have a variable name as first parameter"); } struct node_foreach *fe = xcalloc(1, sizeof(*fe)); n->impl = fe; fe->name = xstrdup(node_value(n->child)); struct xstr *xstr = xstr_create_empty(); const char *v = node_value(n->child->next); if (v && *v != '\0') { xstr_cat(xstr, v); } if (!is_vlist(xstr_ptr(xstr))) { xstr_unshift(xstr, "\1", 1); } fe->items = xstr_destroy_keep_ptr(xstr); } static void _foreach_dispose(struct node *n) { struct node_foreach *fe = n->impl; if (fe) { free(fe->items); free(fe->name); free(fe); } } static void _foreach_init(struct node *n) { } int node_foreach_setup(struct node *n) { n->flags |= NODE_FLG_NO_CWD; n->init = _foreach_init; n->setup = _foreach_setup; n->dispose = _foreach_dispose; return 0; } #ifndef _AMALGAMATE_ #include "script.h" #endif static void _in_sources_init(struct node *n) { // Remove me from the tree struct node *nn = n->child; struct node *prev = node_find_prev_sibling(n); struct node *next = n->next; if (nn) { n->next = nn; n->child = 0; if (prev) { prev->next = nn; } else { n->parent->child = nn; } for ( ; nn; nn = nn->next) { nn->parent = n->parent; if (nn->next == 0) { nn->next = next; break; } } } else if (prev) { prev->next = next; } else { n->parent->child = next; } } int node_in_sources_setup(struct node *n) { n->flags |= NODE_FLG_NO_CWD; n->init = _in_sources_init; return 0; } #ifndef _AMALGAMATE_ #include "script.h" #include "xstr.h" #include "env.h" #include "paths.h" #include "utils.h" #include #endif static void _node_dir_normalize_add(const char *dir, struct xstr *xstr, const char *v, char buf[PATH_MAX]) { if (xstr_size(xstr)) { if (!is_vlist(xstr_ptr(xstr))) { xstr_unshift(xstr, "\1", 1); } xstr_cat2(xstr, "\1", 1); } char *path = path_normalize_cwd(v, dir, buf); xstr_cat(xstr, path); } static const char* _dir_value(struct node *n) { struct node_foreach *fe = node_find_parent_foreach(n); if (fe) { free(n->impl); n->impl = 0; } if (n->impl) { return n->impl; } char buf[PATH_MAX]; struct unit *root = unit_root(); struct unit *unit = unit_peek(); struct xstr *xstr = xstr_create_empty(); const char *dir = 0; if (n->value[0] == 'S') { if (n->value[1] == 'S') { dir = unit->dir; } else { dir = root->dir; } } else { if (n->value[1] == 'C') { dir = unit->cache_dir; } else { dir = root->cache_dir; } } for (struct node *nn = n->child; nn; nn = nn->next) { const char *v = node_value(nn); if (is_vlist(v)) { struct vlist_iter iter; vlist_iter_init(v, &iter); while (vlist_iter_next(&iter)) { if (iter.len) { char vbuf[iter.len + 1]; utils_strnncpy(vbuf, iter.item, iter.len, iter.len + 1); _node_dir_normalize_add(dir, xstr, vbuf, buf); } } } else { _node_dir_normalize_add(dir, xstr, v, buf); } } if (xstr_size(xstr) == 0) { _node_dir_normalize_add(dir, xstr, ".", buf); } n->impl = xstr_destroy_keep_ptr(xstr); return n->impl; } static void _dir_dispose(struct node *n) { if (n->impl) { free(n->impl); } } int node_dir_setup(struct node *n) { n->flags |= NODE_FLG_NO_CWD; n->value_get = _dir_value; n->dispose = _dir_dispose; return 0; } #ifndef _AMALGAMATE_ #include "script.h" #include "xstr.h" #include "env.h" #endif int node_option_setup(struct node *n) { if (!g_env.project.options || !n->child) { return 0; } const char *name = n->child->value; struct xstr *xstr = xstr_create_empty(); for (struct node *nn = n->child->next; nn; nn = nn->next) { if (xstr_size(xstr)) { xstr_cat2(xstr, " ", 1); } xstr_cat(xstr, nn->value); } xstr_printf(g_env.project.options, "%s: %s\n", name, xstr_ptr(xstr)); xstr_destroy(xstr); return 0; } #ifndef _AMALGAMATE_ #include "script.h" #include "xstr.h" #include "log.h" #endif static void _error_setup(struct node *n) { struct xstr *xstr = xstr_create_empty(); for (struct node *nn = n->child; nn; nn = nn->next) { if (node_value(nn)) { if (xstr_size(xstr)) { xstr_cat2(xstr, " ", 1); } xstr_cat(xstr, node_value(nn)); } } node_fatal(AK_ERROR_FAIL, n, xstr_ptr(xstr)); } int node_error_setup(struct node *n) { n->setup = _error_setup; return 0; } #ifndef _AMALGAMATE_ #include "script.h" #include "alloc.h" #include "xstr.h" #include "utils.h" #include #endif struct _echo_ctx { struct node *n_init; struct node *n_setup; struct node *n_build; }; static void _echo_item(struct xstr *xstr, const char *v, size_t len) { if (is_vlist(v)) { struct vlist_iter iter; vlist_iter_init(v, &iter); while (vlist_iter_next(&iter)) { _echo_item(xstr, iter.item, iter.len); } } else { if (xstr_size(xstr)) { xstr_cat2(xstr, " ", 1); } xstr_cat2(xstr, v, len); } } static void _echo(struct node *n) { struct xstr *xstr = xstr_create_empty(); for (struct node *nn = n->child; nn; nn = nn->next) { if (node_is_can_be_value(nn)) { const char *v = node_value(nn); if (v) { _echo_item(xstr, v, strlen(v)); } } } if (g_env.check.log) { xstr_printf(g_env.check.log, "%s: %s\n", n->name, xstr_ptr(xstr)); } node_info(n, "%s", xstr_ptr(xstr)); xstr_destroy(xstr); } static void _echo_init(struct node *n) { struct _echo_ctx *ctx = n->impl; _echo(ctx->n_init); } static void _echo_setup(struct node *n) { struct _echo_ctx *ctx = n->impl; _echo(ctx->n_setup); } static void _echo_build(struct node *n) { struct _echo_ctx *ctx = n->impl; _echo(ctx->n_build); } static void _echo_dispose(struct node *n) { free(n->impl); } int node_echo_setup(struct node *n) { struct _echo_ctx *ctx = xcalloc(1, sizeof(*ctx)); n->impl = ctx; n->dispose = _echo_dispose; for (struct node *nn = n->child; nn; nn = nn->next) { if (nn->type == NODE_TYPE_BAG) { if (!n->build && strcmp(nn->value, "build") == 0) { n->build = _echo_build; ctx->n_build = nn; } else if (!n->setup && strcmp(nn->value, "setup") == 0) { n->setup = _echo_setup; ctx->n_setup = nn; } else if (!n->init && strcmp(nn->value, "init") == 0) { n->init = _echo_init; ctx->n_init = nn; } } } if (!(n->init || n->setup || n->build)) { n->build = _echo_build; ctx->n_build = n; } return 0; } #ifndef _AMALGAMATE_ #include "script.h" #include "ulist.h" #include "env.h" #include "log.h" #include "paths.h" #include "pool.h" #include "utils.h" #include #include #include #include #include #include #include #endif struct _install_on_resolve_ctx { struct node_resolve *r; struct node *n; struct node *n_target; struct ulist consumes; // sizeof(char*) bool install_overlays; }; static void _install_symlink(struct node *n, const char *src, const char *dst, struct stat *st) { char buf[PATH_MAX]; node_info(n, "Symlink %s => %s", src, dst); ssize_t len = readlink(src, buf, sizeof(buf) - 1); if (len == -1) { node_fatal(errno, n, "Error reading symlink: %s", src); } buf[len] = '\0'; if (symlink(buf, dst) == -1) { if (errno == EEXIST) { unlink(dst); if (symlink(buf, dst) == -1) { node_fatal(errno, n, "Error creating symlink: %s", dst); } } else { node_fatal(errno, n, "Error creating symlink: %s", dst); } } #ifdef __APPLE__ struct timespec times[2] = { st->st_atime, st->st_mtime }; #else struct timespec times[2] = { st->st_atim, st->st_mtim }; #endif utimensat(AT_FDCWD, dst, times, AT_SYMLINK_NOFOLLOW); } static void _install_file(struct node *n, const char *src, const char *dst, struct stat *st) { node_info(n, "File %s => %s", src, dst); int in_fd = open(src, O_RDONLY); if (in_fd == -1) { node_fatal(errno, n, "Error opening file: %s", src); } int out_fd = open(dst, O_WRONLY | O_CREAT | O_TRUNC, st->st_mode); if (out_fd == -1) { node_fatal(errno, n, "Error opening file: %s", dst); } char buf[8192]; for (ssize_t r; ; ) { r = read(in_fd, buf, sizeof(buf)); if (r < 0) { if (errno == EAGAIN) { continue; } else { node_fatal(errno, n, "Error reading file: %s", src); } } else if (r == 0) { break; } for (ssize_t w, tow = r; tow > 0; ) { w = write(out_fd, buf + r - tow, tow); if (w >= 0) { tow -= w; } else if (w < 0) { if (errno == EAGAIN) { continue; } node_fatal(errno, n, "Error writing file: %s", dst); } } } fchmod(out_fd, st->st_mode); #ifdef __APPLE__ struct timespec times[2] = { st->st_atime, st->st_mtime }; #else struct timespec times[2] = { st->st_atim, st->st_mtim }; #endif futimens(out_fd, times); close(in_fd); close(out_fd); } static void _install_do(struct _install_on_resolve_ctx *ctx, char *src, const char *target) { char src_buf[PATH_MAX]; char dst_buf[PATH_MAX]; struct stat st; struct node *n = ctx->n; if (ctx->install_overlays) { if (utils_endswith(src, "/" AUTARK_FETCH_DEP)) { // autark-cache/.overlay/myproj/.autark-fetch-dep = autark-cache/.overlay/myproj src = path_dirname(src); snprintf(dst_buf, sizeof(dst_buf), "%s/" AUTARK_CACHE "/" AUTARK_CACHE_OVERLAY_DIR, target); path_mkdirs(dst_buf); int rci = utils_copy_dir_to_parent(src, dst_buf); if (rci) { node_fatal(rci, n, "Failed copy %s directory into %s", src, target); } return; } else if (utils_endswith(src, "/" AUTARK_FETCHED_REG)) { snprintf(dst_buf, sizeof(dst_buf), "%s/" AUTARK_CACHE "/" AUTARK_CACHE_OVERLAY_DIR "/" AUTARK_FETCHED_REG_DIST, target); path_mkdirs_for(dst_buf); _install_file(n, src, dst_buf, &st); return; } } if (lstat(src, &st) || (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode) && !S_ISDIR(st.st_mode))) { node_fatal(AK_ERROR_FAIL, n, "Cannot install unsupported or non accessible file. File: %s", src); } utils_strncpy(src_buf, src, sizeof(src_buf)); snprintf(dst_buf, sizeof(dst_buf), "%s/%s", target, path_basename(src_buf)); if (S_ISREG(st.st_mode)) { path_mkdirs_for(dst_buf); _install_file(n, src, dst_buf, &st); } else if (S_ISLNK(st.st_mode)) { _install_symlink(n, src, dst_buf, &st); } else if (S_ISDIR(st.st_mode)) { int rci = utils_copy_dir_to_parent(src, target); if (rci) { node_fatal(rci, n, "Failed copy %s directory into %s", src, target); } } } static void _install_dep_add(struct deps *deps, const char *src, const char *target) { char src_buf[PATH_MAX]; char dst_buf[PATH_MAX]; deps_add(deps, DEPS_TYPE_FILE, 's', src, 0); utils_strncpy(src_buf, src, sizeof(src_buf)); snprintf(dst_buf, sizeof(dst_buf), "%s/%s", target, path_basename(src_buf)); deps_add_alias(deps, 's', src, dst_buf); } static void _install_on_resolve(struct node_resolve *r) { struct deps deps; struct node *n = r->n; struct _install_on_resolve_ctx *ctx = r->user_data; const char *target = node_value(ctx->n_target); if (!path_is_absolute(target)) { target = pool_printf(r->pool, "%s/%s", g_env.install.prefix_dir, target); target = path_normalize_pool(target, g_env.pool); } if (path_is_exist(target) && !path_is_dir(target)) { node_fatal(AK_ERROR_FAIL, n, "Target: %s is not a directory", target); } int rc = path_mkdirs(target); if (rc) { node_fatal(rc, n, "Failed to create target install directory: %s", target); } struct ulist rlist = { .usize = sizeof(char*) }; struct ulist *slist = &ctx->consumes; if (r->resolve_outdated.num) { for (int i = 0; i < r->resolve_outdated.num; ++i) { struct resolve_outdated *u = ulist_get(&r->resolve_outdated, i); if (u->flags != 's') { // Rebuild all on any outdated non source dependency slist = &ctx->consumes; break; } else { char *path = pool_strdup(r->pool, u->path); ulist_push(&rlist, &path); slist = &rlist; } } } for (int i = 0; i < slist->num; ++i) { char *src = *(char**) ulist_get(slist, i); _install_do(ctx, src, target); } rc = deps_open(r->deps_path_tmp, 0, &deps); if (rc) { node_fatal(rc, n, "Failed to open dependency file: %s", r->deps_path_tmp); } node_add_unit_deps(n, &deps); deps_add_env(&deps, 0, "INSTALL_PREFIX", g_env.install.prefix_dir); for (int i = 0; i < r->node_val_deps.num; ++i) { struct node *nv = *(struct node**) ulist_get(&r->node_val_deps, i); const char *val = node_value(nv); if (val) { deps_add(&deps, DEPS_TYPE_NODE_VALUE, 0, val, i); } } for (int i = 0; i < ctx->consumes.num; ++i) { const char *src = *(const char**) ulist_get(&ctx->consumes, i); _install_dep_add(&deps, src, target); } deps_close(&deps); ulist_destroy_keep(&rlist); } static void _install_on_consumed_resolved(const char *path_, void *d) { struct _install_on_resolve_ctx *ctx = d; const char *path = pool_strdup(ctx->r->pool, path_); ulist_push(&ctx->consumes, &path); } static void _install_on_resolve_init(struct node_resolve *r) { struct _install_on_resolve_ctx *ctx = r->user_data; ctx->r = r; struct node *nn = ctx->n_target->next; if (nn) { node_consumes_resolve(r->n, nn, 0, _install_on_consumed_resolved, ctx); } if (ctx->install_overlays) { DIR *dir = opendir(g_env.project.cache_overlay_dir); if (dir) { for (struct dirent *entry; (entry = readdir(dir)) != 0; ) { const char *name = entry->d_name; if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) { continue; } if (strcmp(name, AUTARK_FETCHED_REG) == 0) { const char *file = pool_printf(r->pool, "%s/" AUTARK_FETCHED_REG, g_env.project.cache_overlay_dir); ulist_push(&ctx->consumes, &file); } else { const char *file = pool_printf(r->pool, "%s/%s/" AUTARK_FETCH_DEP, g_env.project.cache_overlay_dir, name); if (path_is_file(file)) { ulist_push(&ctx->consumes, &file); } } } closedir(dir); } } } static void _install_post_build(struct node *n) { struct _install_on_resolve_ctx ctx = { .n = n, .n_target = n->child, .consumes = { .usize = sizeof(char*) }, .install_overlays = ( n->type == NODE_TYPE_INSTALL_SOURCES && g_env.project.cache_overlay_dir != 0 && !(g_env.install.flags & INSTALL_FLG_SRC_OVERLAYS_APPLIED)) }; if (!ctx.n_target || !node_is_can_be_value(ctx.n_target)) { node_fatal(AK_ERROR_SCRIPT_SYNTAX, n, "No target dir specified"); } if (n->type != NODE_TYPE_INSTALL_SOURCES) { if (!ctx.n_target->next || !node_is_can_be_value(ctx.n_target->next)) { node_fatal(AK_ERROR_SCRIPT_SYNTAX, n, "At least one source file/dir should be specified"); } } struct node_resolve r = { .n = n, .path = n->vfile, .user_data = &ctx, .on_init = _install_on_resolve_init, .on_resolve = _install_on_resolve, .node_val_deps = { .usize = sizeof(struct node*) } }; for (struct node *nn = ctx.n_target; nn; nn = nn->next) { if (node_is_value_may_be_dep_saved(nn, NODE_TYPE_VALUE)) { ulist_push(&r.node_val_deps, &nn); } } node_resolve(&r); ulist_destroy_keep(&ctx.consumes); if (ctx.install_overlays) { g_env.install.flags |= INSTALL_FLG_SRC_OVERLAYS_APPLIED; } } int node_install_setup(struct node *n) { if (!g_env.install.enabled || !g_env.install.prefix_dir) { return 0; } if (n->type == NODE_TYPE_INSTALL_SOURCES) { n->flags |= NODE_FLG_IN_SRC | NODE_FLG_PREFER_SRC_RESOLVING; } else { n->flags |= NODE_FLG_IN_CACHE; } n->post_build = _install_post_build; return 0; } #ifndef _AMALGAMATE_ #include "script.h" #include "xstr.h" #include "env.h" #include "utils.h" #include "paths.h" #include "alloc.h" #include #endif static void _library_find(struct node *n) { struct node *nn = n->child; struct unit *unit = unit_peek(); struct xstr *names = xstr_create_empty(); for (nn = nn->next; nn; nn = nn->next) { if (node_is_can_be_value(nn)) { const char *val = node_value(nn); if (val && *val != '\0') { xstr_printf(names, "\1%s", val); } } } struct xstr *dirs = xstr_create_empty(); const char *ldir = env_libdir(); bool sld = strcmp(ldir, "lib") != 0; xstr_printf(dirs, "\1%s/%s/", unit->cache_dir, ldir); if (sld) { xstr_printf(dirs, "\1%s/lib/", unit->cache_dir); } if (strcmp(unit->cache_dir, g_env.project.cache_dir) != 0) { xstr_printf(dirs, "\1%s/%s/", g_env.project.cache_dir, ldir); if (sld) { xstr_printf(dirs, "\1%s/lib/", g_env.project.cache_dir); } } const char *home = getenv("HOME"); if (home) { xstr_printf(dirs, "\1%s/.local/%s/", home, ldir); if (sld) { xstr_printf(dirs, "\1%s/.local/lib/", home); } } xstr_printf(dirs, "\1/usr/local/%s/", ldir); if (sld) { xstr_cat(dirs, "\1/usr/local/lib/"); } xstr_printf(dirs, "\1/usr/%s/", ldir); if (sld) { xstr_cat(dirs, "\1/usr/lib/"); } xstr_printf(dirs, "\1/%s/", ldir); if (sld) { xstr_cat(dirs, "\1/lib/"); } struct vlist_iter iter_dirs; vlist_iter_init(xstr_ptr(dirs), &iter_dirs); while (vlist_iter_next(&iter_dirs)) { struct vlist_iter iter_names; vlist_iter_init(xstr_ptr(names), &iter_names); while (vlist_iter_next(&iter_names)) { char buf[PATH_MAX]; snprintf(buf, sizeof(buf), "%.*s%.*s", (int) iter_dirs.len, iter_dirs.item, (int) iter_names.len, iter_names.item); if (path_is_exist(buf)) { n->impl = xstrdup(buf); goto finish; } } } finish: xstr_destroy(names); xstr_destroy(dirs); } static const char* _find_value_get(struct node *n) { if (n->impl) { if ((uintptr_t) n->impl == (uintptr_t) -1) { return ""; } else { return n->impl; } } n->impl = (void*) (intptr_t) -1; const char *nv = n->value; while(*nv == '!') ++nv; if (strcmp("library", nv) == 0) { _library_find(n); } if (!n->impl) { n->impl = (void*) (uintptr_t) -1; } if ((uintptr_t) n->impl == (uintptr_t) -1) { return ""; } else { return n->impl; } } static struct unit* _unit_for_find(struct node *n, struct node *nn, const char **keyp) { if (nn->type == NODE_TYPE_BAG) { if (strcmp(nn->value, "root") == 0) { *keyp = node_value(nn->child); return unit_root(); } else if (strcmp(nn->value, "parent") == 0) { *keyp = node_value(nn->child); return unit_parent(n); } } else { *keyp = node_value(nn); } return unit_peek(); } static void _find_init(struct node *n) { const char *key = 0; struct unit *unit = n->child ? _unit_for_find(n, n->child, &key) : 0; if (!key) { node_fatal(AK_ERROR_SCRIPT_SYNTAX, n, "No name specified for '%s' directive", n->value); return; } unit_env_set_node(unit, key, n, TAG_INIT); } static void _find_dispose(struct node *n) { if ((uintptr_t) n->impl != (uintptr_t) -1) { free(n->impl); } n->impl = 0; } int node_find_setup(struct node *n) { n->flags |= NODE_FLG_NO_CWD; n->init = _find_init; n->value_get = _find_value_get; n->dispose = _find_dispose; return 0; } #ifndef _AMALGAMATE_ #include "script.h" #include "log.h" #include "alloc.h" #include "config.h" #endif struct _macro_state { int num_calls; }; void macro_register_call(struct node *mn) { akassert(mn->type == NODE_TYPE_MACRO); struct _macro_state *s = mn->impl; if (!s) { s = xmalloc(sizeof(*s)); s->num_calls = 0; mn->impl = s; } ++s->num_calls; if (s->num_calls >= MACRO_MAX_RECURSIVE_CALLS) { node_fatal(AK_ERROR_MACRO_MAX_RECURSIVE_CALLS, mn, 0); } } void macro_unregister_call(struct node *mn) { akassert(mn->type == NODE_TYPE_MACRO); struct _macro_state *s = mn->impl; akassert(s); --s->num_calls; akassert(s->num_calls >= 0); } static void _macro_remove(struct node *n) { struct node *prev = node_find_prev_sibling(n); if (prev) { prev->next = n->next; } else { n->parent->child = n->next; } } static void _macro_init(struct node *n) { struct unit *unit = unit_peek(); const char *key = node_value(n->child); if (!key || key[0] == '\0') { node_warn(n, "No name specified for 'macro' directive"); return; } _macro_remove(n); unit_env_set_node(unit, key, n, TAG_INIT); } static void _macro_dispose(struct node *n) { struct _macro_state *s = n->impl; if (s) { free(s); } } int node_macro_setup(struct node *n) { n->flags |= NODE_FLG_NO_CWD; n->init = _macro_init; n->dispose = _macro_dispose; return 0; } #ifndef _AMALGAMATE_ #include "script.h" #include "utils.h" #include "alloc.h" #include "ulist.h" #endif #define MACRO_MAX_ARGS_NUM 64 struct _call { const char *key; ///< Macro name struct node *n; ///< Call Parent node struct node *mn; ///< Macro node struct node *prev; ///< Call Prev node struct node *cloned; ///< Macro cloned node struct ulist nodes; ///< Nodes stack. struct node* struct node *args[MACRO_MAX_ARGS_NUM]; int nn_idx; int arg_idx; }; struct node* call_macro_node(struct node *n) { akassert(n->type == NODE_TYPE_CALL); struct _call *c = n->impl; akassert(c && c->mn); return c->mn; } struct node* call_first_node(struct node *n) { akassert(n->type == NODE_TYPE_CALL); struct _call *c = n->impl; akassert(c); if (c->nn_idx > -1) { struct node *nn = *(struct node**) ulist_get(&n->ctx->nodes, c->nn_idx); return nn; } else { return 0; } } static int _call_macro_visit(struct node *n, int lvl, void *d) { struct _call *call = d; int ret = 0; if (call->mn == n /*skip macro itself */ || call->mn->child == n /* skip macro name */) { return 0; } if (lvl < 0) { ulist_pop(&call->nodes); return 0; } // Macro arg if (n->value[0] == '&' && n->value[1] == '\0') { int idx; int rc = 0; if (n->child) { idx = utils_strtol(n->child->value, 10, &rc); if (rc || idx < 1 || idx >= MACRO_MAX_ARGS_NUM) { node_fatal(rc, n, "Invalid macro arg index: %d", idx); return 0; } ret = INT_MAX; idx--; } else { call->arg_idx++; idx = call->arg_idx; } call->arg_idx = idx; n = call->args[idx]; if (!n) { node_fatal(rc, call->n, "Call argument: %d for macro: %s is not set", (idx + 1), call->key); return 0; } } struct node *parent = *(struct node**) ulist_peek(&call->nodes); if (call->nn_idx == -1) { call->nn_idx = n->ctx->nodes.num; } struct node *nn = node_clone_and_register(n); nn->parent = parent; // Add node to the parent if (!parent->child) { parent->child = nn; } else { struct node *c = parent->child; while (c && c->next) { c = c->next; } c->next = nn; } ulist_push(&call->nodes, &nn); return ret; } static void _call_remove(struct node *n) { struct node *prev = node_find_prev_sibling(n); if (prev) { prev->next = n->next; } else { n->parent->child = n->next; } } static void _call_init(struct node *n) { struct unit *unit = unit_peek(); const char *key = node_value(n->child); if (!key || key[0] == '\0') { node_warn(n, "No name specified for 'call' directive"); return; } struct node *mn = unit_env_get_node(unit, key, 0); if (!mn) { node_fatal(0, n, "Unknown script macro: %s", key); return; } struct _call *call = xcalloc(1, sizeof(*call)); call->nn_idx = -1; call->arg_idx = -1; call->key = key; call->mn = mn; call->n = n; call->prev = node_find_prev_sibling(n); ulist_init(&call->nodes, 16, sizeof(struct node*)); n->impl = call; int idx = 0; struct node *next = 0; for (struct node *pn = n->child->next; pn; pn = next) { next = pn->next; if (idx >= MACRO_MAX_ARGS_NUM) { node_fatal(0, n, "Exceeded the maximum number of macro args: " Q_STR(MACRO_MAX_ARGS_NUM)); return; } pn->next = 0; call->args[idx++] = pn; } ulist_push(&call->nodes, &n->parent); _call_remove(n); node_visit(mn, 1, call, _call_macro_visit); for (int i = call->nn_idx; i < n->ctx->nodes.num; ++i) { struct node *nn = *(struct node**) ulist_get(&n->ctx->nodes, i); node_bind(nn); } } static void _call_dispose(struct node *n) { struct _call *call = n->impl; if (call) { ulist_destroy_keep(&call->nodes); free(call); } } int node_call_setup(struct node *n) { n->flags |= NODE_FLG_NO_CWD; n->init = _call_init; n->dispose = _call_dispose; return 0; } #ifndef _AMALGAMATE_ #include "env.h" #include "script.h" #include "paths.h" #include "fetchreg.h" #endif static void _fetch_url_regcb(const struct fetcherg_entry *e, void *d) { struct node *n = d; if (e->target) { struct xstr *xstr = xstr_create_empty(); xstr_printf(xstr, "dir://%s/%s", g_env.project.cache_overlay_dir, e->target); n->impl = xstr_destroy_keep_ptr(xstr); } } static const char* _fetch_url_value_get(struct node *n) { if (n->impl) { return n->impl; } if (!n->child) { return ""; } const char *url = node_value(n->child); if (!g_env.project.cache_overlay_dir) { return url; } char path[PATH_MAX]; snprintf(path, sizeof(path), "%s/" AUTARK_FETCHED_REG_DIST, g_env.project.cache_overlay_dir); if (!path_is_file(path)) { return url; } struct fetchreg *reg; int rc = fetchreg_open(path, ®); if (rc) { node_fatal(rc, n, "Error opening fetched registry: %s", path); } fetchreg_find(reg, url, n, _fetch_url_regcb); fetchreg_close(reg); return n->impl ? n->impl : url; } static void _fetch_url_dispose(struct node *n) { if (n->impl) { free(n->impl); } } int node_fetch_url_setup(struct node *n) { n->flags |= NODE_FLG_NO_CWD; n->value_get = _fetch_url_value_get; n->dispose = _fetch_url_dispose; return 0; } #ifndef _AMALGAMATE_ #ifndef META_VERSION #define META_VERSION "dev" #endif #ifndef META_REVISION #define META_REVISION "" #endif #include "env.h" #include "utils.h" #include "log.h" #include "paths.h" #include "script.h" #include "autark.h" #include "map.h" #include "alloc.h" #include "deps.h" #include "fetchreg.h" #include #include #include #include #include #include #include #include #include #endif struct env g_env; static void _unit_destroy(struct unit *unit) { map_destroy(unit->env); } void unit_env_set_val(struct unit *u, const char *key, const char *val) { size_t len = sizeof(struct unit_env_item); if (val) { len += strlen(val) + 1; } struct unit_env_item *item = xmalloc(len); item->n = 0; if (len > sizeof(struct unit_env_item)) { char *wp = ((char*) item) + sizeof(struct unit_env_item); memcpy(wp, val, len - sizeof(struct unit_env_item)); item->val = wp; } else { item->val = 0; } map_put_str(u->env, key, item); } void unit_env_set_node(struct unit *u, const char *key, struct node *n, unsigned tag) { struct unit_env_item *item = xmalloc(sizeof(*item)); item->val = 0; item->n = n; item->tag = tag; map_put_str(u->env, key, item); } struct node* unit_env_get_node(struct unit *u, const char *key, unsigned *out_tag) { struct unit_env_item *item = map_get(u->env, key); if (item) { if (out_tag) { *out_tag = item->tag; } return item->n; } else if (out_tag) { *out_tag = 0; } return 0; } const char* unit_env_get_raw(struct unit *u, const char *key) { struct unit_env_item *item = map_get(u->env, key); if (item) { if (item->val) { return item->val; } else { return node_value(item->n); } } return 0; } const char* unit_env_get(struct node *n, const char *key) { struct unit *prev = 0; for (struct node *nn = n; nn; nn = nn->parent) { if (nn->unit && nn->unit != prev) { prev = nn->unit; const char *ret = unit_env_get_raw(nn->unit, key); if (ret) { return ret; } } } return getenv(key); } void unit_env_remove(struct unit *u, const char *key) { map_remove(u->env, key); } void on_unit_pool_destroy(struct pool *pool) { for (int i = 0; i < g_env.units.num; ++i) { struct unit *unit = *(struct unit**) ulist_get(&g_env.units, i); if (unit->pool == pool) { map_remove(g_env.map_path_to_unit, unit->source_path); map_remove(g_env.map_path_to_unit, unit->cache_path); _unit_destroy(unit); ulist_remove(&g_env.units, i); --i; } } } struct unit* unit_for_path(const char *path) { struct unit *unit = map_get(g_env.map_path_to_unit, path); return unit; } struct unit* unit_create(const char *unit_path_, unsigned flags, struct pool *pool) { char path[PATH_MAX]; const char *unit_path = unit_path_; if (path_is_absolute(unit_path)) { const char *p = path_is_prefix_for(g_env.project.cache_dir, unit_path, 0); if (p) { unit_path = p; } else { p = path_is_prefix_for(g_env.project.root_dir, unit_path, 0); if (!p) { akfatal(AK_ERROR_FAIL, "Unit path: %s must be either in project root or cache directory", unit_path_); } unit_path = p; } } struct unit *unit = pool_calloc(g_env.pool, sizeof(*unit)); unit->flags = flags; unit->pool = pool; unit->env = map_create_str(map_kv_free); unit->rel_path = pool_strdup(pool, unit_path); unit->basename = path_basename((char*) unit->rel_path); unit->source_path = pool_printf(pool, "%s/%s", g_env.project.root_dir, unit_path); utils_strncpy(path, unit->source_path, sizeof(path)); unit->dir = pool_strdup(pool, path_dirname(path)); unit->cache_path = pool_printf(pool, "%s/%s", g_env.project.cache_dir, unit_path); utils_strncpy(path, unit->cache_path, sizeof(path)); unit->cache_dir = pool_strdup(pool, path_dirname(path)); int rc = path_mkdirs(unit->cache_dir); if (rc) { akfatal(rc, "Failed to create directory: %s", path); } map_put_str_no_copy(g_env.map_path_to_unit, unit->source_path, unit); map_put_str_no_copy(g_env.map_path_to_unit, unit->cache_path, unit); ulist_push(&g_env.units, &unit); if (g_env.verbose) { if (unit->flags & UNIT_FLG_ROOT) { akinfo("%s: AUTARK_ROOT_DIR=%s", unit->rel_path, unit->dir); akinfo("%s: AUTARK_CACHE_DIR=%s", unit->rel_path, unit->cache_dir); } else { akinfo("%s: UNIT_DIR=%s", unit->rel_path, unit->dir); akinfo("%s: UNIT_CACHE_DIR=%s", unit->rel_path, unit->cache_dir); } } if (unit->flags & UNIT_FLG_ROOT) { unit_env_set_val(unit, "AUTARK_ROOT_DIR", unit->dir); unit_env_set_val(unit, "AUTARK_CACHE_DIR", unit->cache_dir); } unit_env_set_val(unit, "UNIT_DIR", unit->dir); unit_env_set_val(unit, "UNIT_CACHE_DIR", unit->cache_dir); return unit; } void unit_push(struct unit *unit, struct node *n) { akassert(unit); struct unit_ctx ctx = { unit, 0 }; if (n) { ctx.flags = n->flags; } ulist_push(&g_env.stack_units, &ctx); unit_ch_dir(&ctx, 0); setenv(AUTARK_UNIT_ENV, unit->rel_path, 1); } struct unit* unit_pop(void) { akassert(g_env.stack_units.num > 0); struct unit_ctx *ctx = (struct unit_ctx*) ulist_get(&g_env.stack_units, g_env.stack_units.num - 1); ulist_pop(&g_env.stack_units); struct unit_ctx peek = unit_peek_ctx(); if (peek.unit) { unit_ch_dir(&peek, 0); setenv(AUTARK_UNIT_ENV, peek.unit->rel_path, 1); } else { unsetenv(AUTARK_UNIT_ENV); } return ctx->unit; } struct unit* unit_peek(void) { struct unit *u = unit_peek_ctx().unit; akassert(u); return u; } struct unit* unit_root(void) { akassert(g_env.stack_units.num); struct unit_ctx uc = *(struct unit_ctx*) ulist_get(&g_env.stack_units, 0); return uc.unit; } struct unit* unit_parent(struct node *n) { struct unit *u = n->unit; for (struct node *nn = n->parent; nn; nn = nn->parent) { if (!u) { u = nn->unit; } else if (nn->unit && n->unit != nn->unit) { return nn->unit; } } return unit_root(); } struct unit_ctx unit_peek_ctx(void) { if (g_env.stack_units.num == 0) { return (struct unit_ctx) { 0, 0 }; } return *(struct unit_ctx*) ulist_get(&g_env.stack_units, g_env.stack_units.num - 1); } void unit_ch_dir(struct unit_ctx *ctx, char *prevcwd) { if (ctx->flags & NODE_FLG_IN_ANY) { if (ctx->flags & NODE_FLG_IN_SRC) { unit_ch_src_dir(ctx->unit, prevcwd); } else if (ctx->flags & NODE_FLG_IN_CACHE) { unit_ch_cache_dir(ctx->unit, prevcwd); } } else if (ctx->unit->flags & UNIT_FLG_SRC_CWD) { unit_ch_src_dir(ctx->unit, prevcwd); } else if (!(ctx->unit->flags & UNIT_FLG_NO_CWD)) { unit_ch_cache_dir(ctx->unit, prevcwd); } } void unit_ch_cache_dir(struct unit *unit, char *prevcwd) { akassert(unit); if (prevcwd) { akassert(getcwd(prevcwd, PATH_MAX)); } akcheck(chdir(unit->cache_dir)); } void unit_ch_src_dir(struct unit *unit, char *prevcwd) { akassert(unit); if (prevcwd) { akassert(getcwd(prevcwd, PATH_MAX)); } akcheck(chdir(unit->dir)); } static int _usage_va( const char *err, va_list ap) { if (err) { fprintf(stderr, "\nError: "); vfprintf(stderr, err, ap); fprintf(stderr, "\n\n"); } fprintf(stderr, "\nautark [options] [sources_dir] | [command [options]]\n"); fprintf(stderr, "\n -V, --verbose Outputs verbose execution info.\n" " -v, --version Prints version info.\n" " -h, --help Prints usage help.\n"); fprintf(stderr, " -H, --cache=<> Project cache/build dir. Default: ./" AUTARK_CACHE "\n"); fprintf(stderr, " -c, --clean Clean build cache dir.\n"); fprintf(stderr, " -l, --options List of all available project options and their description.\n"); fprintf(stderr, " -J --jobs=<> Number of jobs used in c/cxx compilation tasks. Default: 4\n"); fprintf(stderr, " -D