#include #include #include #include "libs.h" #include "log.h" #include "db.h" #include "dep.h" /* Returns true if the first 2 characters of the output is OK */ static int try_icl_accept_ok(char *stdout_str) { return (strncmp(stdout_str, "OK", 2) == 0); } #define is_ctrl_prefix(ch) (((ch) == '!') || ((ch) == '^')) static int try_icl__(int logdepth, const char *prefix, const char *test_c_in, const char *includes, const char *cflags, const char *ldflags, const char *db_includes, const char *db_cflags, const char *db_ldflags, int run, int (*accept_res)(char *stdout_str)) { char *out = NULL; char *tmp, *inc; const char *test_c; char c[1024]; int l, compres; if (includes != NULL) { l = strlen(includes); memcpy(c, includes, l); c[l] = '\n'; l++; strcpy(c+l, test_c_in); test_c = c; } else test_c = test_c_in; logprintf(logdepth, "trying '%s' and '%s' and '%s', %s\n", str_null(db_includes), str_null(db_cflags), str_null(db_ldflags), run ? "with a run" : "with no run"); if (run) compres = compile_run(logdepth+1, test_c, NULL, cflags, ldflags, &out); else { char *fn_output = NULL; compres = compile_code(logdepth+1, test_c, &fn_output, NULL, cflags, ldflags); if (fn_output != NULL) { unlink(fn_output); free(fn_output); } } if (compres == 0) { if (!run || target_emu_fail(out) || accept_res(out)) { free(out); /* no prefix: don't modify the database, the caller will do that */ if (prefix == NULL) return 1; tmp = malloc(strlen(prefix) + 32); if ((db_includes == NULL) || (*db_includes == '\0')) inc = strclone(""); else inc = uniq_inc_str(db_includes, NULL, "\\n", 0, 0, NULL); sprintf(tmp, "%s/includes", prefix); put(tmp, inc); if (db_cflags == NULL) db_cflags = ""; sprintf(tmp, "%s/cflags", prefix); put(tmp, db_cflags); if (db_ldflags == NULL) db_ldflags = ""; sprintf(tmp, "%s/ldflags", prefix); put(tmp, db_ldflags); if (inc != NULL) { report("OK ('%s', '%s' and '%s')\n", str_null(inc), str_null(db_cflags), str_null(db_ldflags)); free(inc); } else report("OK ('%s' and '%s')\n", str_null(db_cflags), str_null(db_ldflags)); sprintf(tmp, "%s/presents", prefix); put(tmp, strue); free(tmp); return 1; } free(out); } return 0; } #define LOAD(node) \ do { \ if (u ## node != NULL) break; \ strcpy(apath_end, #node); \ u ## node = get(apath); \ } while(0) #define SET(dst, src, is_flag) \ do { \ char *__sep__ = is_flag ? " " : "\n"; \ const char *__dst__ = dst == NULL ? "" : dst; \ char *__out__; \ if (is_ctrl_prefix(*__dst__)) __dst__++; \ if (*src == '!') \ __out__ = strclone(src+1); \ else if (*src == '^') {\ if (src[1] != '\0') \ __out__ = str_concat("", src+1, __sep__, __dst__, NULL); \ else \ __out__ = strclone(__dst__); \ } \ else { \ if (*__dst__ != '\0') \ __out__ = str_concat("", __dst__, __sep__, src, NULL); \ else \ __out__ = strclone(src); \ } \ free(dst); \ dst = __out__; \ if (is_flag) { \ char *__s__; \ for(__s__ = dst; *__s__ != '\0'; __s__++) \ if ((*__s__ == '\n') || (*__s__ == '\r')) \ *__s__ = ' '; \ } \ } while(0) /* Figure user overrides and call try_icl__() accordingly */ int try_icl_(int logdepth, const char *prefix, const char *test_c_in, const char *includes, const char *cflags, const char *ldflags, int run, int (*accept_res)(char *stdout_str)) { char apath[1024], *apath_end; int l, res; const char *uincludes = NULL, *ucflags = NULL, *uldflags = NULL, *uprefix = NULL; /* user specified */ char *rincludes, *rcflags, *rldflags; /* real */ char *dbincludes = NULL, *dbcflags = NULL, *dbldflags = NULL; /* what to add in the db at the end */ if ((prefix == NULL) ? 0 : strlen(prefix) + strlen(db_cwd) > sizeof(apath)-32) { report("ERROR: no room for try_icl_() - prefix is probably too long.\n"); return -1; } /* load uincludes, uclfags, uldflags and uprefix - LOAD() inserts the u */ if (prefix != NULL) { /* prefix == NULL means non-standard icl; caller sets non-standard nodes, the user can't affect that */ l = sprintf(apath, "/arg/icl/%s/", prefix); apath_end = apath+l; LOAD(includes); LOAD(cflags); LOAD(ldflags); LOAD(prefix); l = sprintf(apath, "/arg/icl/%s/%s/", db_cwd, prefix); apath_end = apath+l; LOAD(includes); LOAD(cflags); LOAD(ldflags); LOAD(prefix); } /* special case: all three specified by the user - ignore what the detector wanted, but run only once per node prefix */ if ((uincludes != NULL) && (ucflags != NULL) && (uldflags != NULL)) { const char *am; sprintf(apath, "%s/icl/all_manual_result", prefix); am = get(apath); if (am != NULL) return istrue(am); /* return cached result if available */ res = try_icl__(logdepth, prefix, test_c_in, uincludes, ucflags, uldflags, uincludes, ucflags, uldflags, run, accept_res); put(apath, res ? strue : sfalse); return res; } /* TODO: get default cflags here */ rincludes = NULL; rcflags = strclone(get("cc/cflags")); rldflags = strclone(get("cc/ldflags")); /* override base/default values with detection requested ones */ if (includes != NULL) SET(rincludes, includes, 0); if (cflags != NULL) SET(rcflags, cflags, 1); if (ldflags != NULL) SET(rldflags, ldflags, 1); if (includes != NULL) SET(dbincludes, includes, 0); if (cflags != NULL) SET(dbcflags, cflags, 1); if (ldflags != NULL) SET(dbldflags, ldflags, 1); /* override detection with user specified ICL values */ if (uincludes != NULL) SET(rincludes, uincludes, 0); if (ucflags != NULL) SET(rcflags, ucflags, 1); if (uldflags != NULL) SET(rldflags, uldflags, 1); if (uincludes != NULL) SET(dbincludes, uincludes, 0); if (ucflags != NULL) SET(dbcflags, ucflags, 1); if (uldflags != NULL) SET(dbldflags, uldflags, 1); /* insert prefix as needed */ if (uprefix != NULL) { char *old, *prfx; old = rcflags; if ((rcflags != NULL) && (*rcflags == '^')) { rcflags++; prfx = "^"; } else prfx = ""; rcflags = str_concat("", prfx, "-I", uprefix, "/include ", rcflags, NULL); if (old != cflags) free(old); /* add -I to the db too */ old = dbcflags; dbcflags = str_concat("", "-I", uprefix, "/include ", dbcflags, NULL); free(old); old = rldflags; if ((rldflags != NULL) && (*rldflags == '^')) { rldflags++; prfx = "^"; } else prfx = ""; rldflags = str_concat("", prfx, "-L", uprefix, "/lib ", rldflags, NULL); if (old != ldflags) free(old); /* add -L to the db too */ old = dbldflags; dbldflags = str_concat("", "-L", uprefix, "/lib ", dbldflags, NULL); free(old); } res = try_icl__(logdepth, prefix, test_c_in, rincludes, rcflags, rldflags, dbincludes, dbcflags, dbldflags, run, accept_res); /* if we had to alloc, free here */ free(rincludes); free(rcflags); free(rldflags); free(dbincludes); free(dbcflags); free(dbldflags); return res; } #undef LOAD #undef SET int try_icl(int logdepth, const char *prefix, const char *test_c_in, const char *includes, const char *cflags, const char *ldflags) { return try_icl_(logdepth, prefix, test_c_in, includes, cflags, ldflags, 1, try_icl_accept_ok); } int try_icl_with_deps(int logdepth, const char *prefix, const char *test_c_in, const char *includes, const char *cflags, const char *ldflags, const char *dep_includes, const char *dep_cflags, const char *dep_ldflags, int run) { int res; if ((dep_includes != NULL) && (*dep_includes == '\0')) dep_includes = NULL; if ((dep_cflags != NULL) && (*dep_cflags == '\0')) dep_cflags = NULL; if ((dep_ldflags != NULL) && (*dep_ldflags == '\0')) dep_ldflags = NULL; if (dep_includes != NULL) includes = str_concat(" ", dep_includes, includes, NULL); if (dep_cflags != NULL) cflags = str_concat(" ", dep_cflags, cflags, NULL); if (dep_ldflags != NULL) ldflags = str_concat(" ", dep_ldflags, ldflags, NULL); res = try_icl_(logdepth, prefix, test_c_in, includes, cflags, ldflags, run, try_icl_accept_ok); if (dep_includes != NULL) free((char *)includes); if (dep_cflags != NULL) free((char *)cflags); if (dep_ldflags != NULL) free((char *)ldflags); return res; } int try_icl_norun(int logdepth, const char *prefix, const char *test_c_in, const char *includes, const char *cflags, const char *ldflags) { return try_icl_(logdepth, prefix, test_c_in, includes, cflags, ldflags, 0, try_icl_accept_ok); } int try_fail(int logdepth, const char *prefix) { char *tmp; tmp = malloc(strlen(prefix) + 32); sprintf(tmp, "%s/presents", prefix); put(tmp, sfalse); free(tmp); report("not found\n"); logprintf(logdepth, "NOT FOUND."); return 1; } static int try_pkg_config_(int logdepth, char *pkgname, const char *prefix, const char *test_c) { char *cflags, *ldflags; int res; logprintf(logdepth, "Trying pkg-config %s\n", pkgname); if (run_pkg_config(logdepth+1, pkgname, &cflags, &ldflags) == 0) res = try_icl(logdepth+1, prefix, test_c, NULL, cflags, ldflags); else res = 0; free(cflags); free(ldflags); return res; } int try_icl_pkg_config(int logdepth, const char *prefix, const char *test_c, char *includes, const char *pkgpat, const char *reqver) { char **pkg_ver, **s; int num_pkg_ver; int res = 0; (void) includes; /* not used */ run_pkg_config_lst(logdepth, pkgpat, &num_pkg_ver, &pkg_ver); if (pkg_ver == NULL) return 0; if (reqver != NULL) { /* search the list for the preferred version */ for(s = pkg_ver; *s != NULL; s+=2) { if (strcmp(s[1], reqver) == 0) { if (try_pkg_config_(logdepth, s[0], prefix, test_c)) { res = 1; report("Found version required (%s) using pkg_config.\n", reqver); goto out; } else { report("The version required (%s) is found (via pkg_config) but does not work\n", reqver); goto out; } } } goto out; } for(s = pkg_ver; *s != NULL; s+=2) { if (try_pkg_config_(logdepth, s[0], prefix, test_c)) { res = 1; goto out; } } out:; filelist_free(&num_pkg_ver, &pkg_ver); return res; } int import_icl(const char *key, const char *fn) { char path[1024]; if (strlen(key) > sizeof(path)-32) { report("ERROR: no room for import_icl() - key is probably too long.\n"); return -1; } switch(*key) { case 'l': sprintf(path, "/arg/icl/%s/ldflags", key+8); break; case 'c': sprintf(path, "/arg/icl/%s/cflags", key+7); break; case 'i': sprintf(path, "/arg/icl/%s/includes", key+9); break; case 'p': sprintf(path, "/arg/icl/%s/prefix", key+7); break; default: return 1; } return put(path, fn) == NULL; } static long field_accept_len; static int field_accept_res(char *stdout_str) { char *end; field_accept_len = strtol(stdout_str, &end, 10); if (((*end == '\0') || (*end == '\r') || (*end == '\n')) && (field_accept_len > 0)) return 1; return 0; } int try_icl_sfield(int logdepth, const char *prefix, const char *structn, const char *fieldn, const char *includes, const char *cflags, const char *ldflags) { int res; char test_c[512]; char ls[16]; const char *test_c_in = NL "#include " NL "int main()" NL "{" NL " %s s;" NL " printf(\"%%ld\\n\", (long)sizeof(s.%s));" NL "}" NL; if (strlen(fieldn) + strlen(structn) + strlen(test_c_in) + 32 >= sizeof(test_c)) { report("ERROR: no room for try_icl_sfield() - struct or field name is probably too long.\n"); return -1; } sprintf(test_c, test_c_in, structn, fieldn); res = try_icl_(logdepth, prefix, test_c, includes, cflags, ldflags, 1, field_accept_res); if (res) { sprintf(test_c, "%s/sizeof", prefix); sprintf(ls, "%ld", field_accept_len); put(test_c, ls); } return res; } int try_icl_sfields(int logdepth, const char *prefix, const char *structn, const char **fields, const char *includes, const char *cflags, const char *ldflags, int silent_exit_first_fail) { int succ = 0, first = 1; require("cc/cc", logdepth, 1); for(; *fields != NULL; fields++) { report("Checking for %s.%s... ", structn, *fields); logprintf(logdepth, "%s: checking for field %s...\n", structn, *fields); logdepth++; if (try_icl_sfield(logdepth, prefix, structn, *fields, includes, cflags, ldflags)) { succ = 1; } else if ((silent_exit_first_fail) && (first)) { return 1; } logdepth--; first = 0; } if (!succ) try_fail(logdepth, "libs/fsmount/next_dev"); return 0; }