497 lines
13 KiB
C
497 lines
13 KiB
C
/*
|
|
scconfig - detect features of the system or the host/target computer
|
|
Copyright (C) 2009..2012 Tibor Palinkas
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2.1 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with this library; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
Project page: http://repo.hu/projects/scconfig
|
|
Contact (email and IRC): http://igor2.repo.hu/contact.html
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <unistd.h>
|
|
#include "libs.h"
|
|
#include "log.h"
|
|
#include "db.h"
|
|
#include "dep.h"
|
|
|
|
int find_sys_ptrwidth(const char *name, int logdepth, int fatal)
|
|
{
|
|
char *end, W[32];
|
|
char *out = NULL;
|
|
int w;
|
|
|
|
char *test_c =
|
|
NL "#include <stdio.h>"
|
|
NL "int main() {"
|
|
NL " void *ptr;"
|
|
NL " printf(\"%d\\n\", sizeof(ptr));"
|
|
NL " return 0;"
|
|
NL "}"
|
|
NL;
|
|
|
|
require("cc/cc", logdepth, fatal);
|
|
|
|
report("Checking for pointer width... ");
|
|
logprintf(logdepth, "find_sys_ptrwidth: trying to find pointer width...\n");
|
|
logdepth++;
|
|
|
|
if (compile_run(logdepth, test_c, NULL, NULL, NULL, &out) == 0) {
|
|
w = strtol(out, &end, 10);
|
|
if ((*end != '\0') && (*end != '\n') && (*end != '\r')) {
|
|
report("FAILED (test code failed)\n");
|
|
logprintf(logdepth+1, "FAILED: returned '%s' which is not a valid decimal number (at '%s')\n", out, end);
|
|
return 1;
|
|
}
|
|
sprintf(W, "%d", w * 8);
|
|
report("OK (%s bits)\n", W);
|
|
put("sys/ptrwidth", W);
|
|
logprintf(logdepth+1, "OK (%s bits)\n", W);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int find_sys_byte_order(const char *name, int logdepth, int fatal)
|
|
{
|
|
const char *test_c =
|
|
NL "#include <stdio.h>"
|
|
NL "int main() {"
|
|
NL " long int i = 8;"
|
|
NL " char *s = (char *)&i;"
|
|
NL " printf(\"%d\\n\", s[0]);"
|
|
NL " return 0;"
|
|
NL "}"
|
|
NL;
|
|
const char* test_c_blind_template =
|
|
NL "#include <stdio.h>"
|
|
NL "#include <endian.h>"
|
|
NL "#ifndef __BYTE_ORDER"
|
|
NL "#error \"ERROR 1\""
|
|
NL "void void *;"
|
|
NL "#endif"
|
|
NL "#ifndef __%s_ENDIAN"
|
|
NL "#error \"ERROR 2\""
|
|
NL "char char *;"
|
|
NL "#endif"
|
|
NL "#if __BYTE_ORDER != __%s_ENDIAN"
|
|
NL "#error \"ERROR 3\""
|
|
NL "int int *;"
|
|
NL "#endif"
|
|
NL "int main() {"
|
|
NL " puts(\"OK\");"
|
|
NL " return 0;"
|
|
NL "}"
|
|
NL;
|
|
const char *key = "sys/byte_order";
|
|
const char *endians1[] = { "LITTLE", "BIG", NULL };
|
|
const char *endians2[] = { "LSB", "MSB", NULL };
|
|
int index;
|
|
char test_c_blind[1024];
|
|
char *end;
|
|
const char *W;
|
|
char *out = NULL;
|
|
int w;
|
|
|
|
require("cc/cc", logdepth, fatal);
|
|
|
|
report("Checking for byte order... ");
|
|
logprintf(logdepth, "find_sys_byte_order: trying to find byte order...\n");
|
|
logdepth++;
|
|
|
|
if ((!isblind(db_cwd)) && compile_run(logdepth, test_c, NULL, NULL, NULL, &out) == 0) {
|
|
w = strtol(out, &end, 10);
|
|
if (((*end != '\0') && (*end != '\n') && (*end != '\r')) || ((w != 0) && (w != 8))) {
|
|
report("FAILED (test code failed)\n");
|
|
logprintf(logdepth+1, "FAILED: returned '%s' which is not a valid decimal number (at '%s')\n", out, end);
|
|
return 1;
|
|
}
|
|
if (w == 0)
|
|
W = "MSB";
|
|
else
|
|
W = "LSB";
|
|
|
|
report("OK (%s first)\n", W);
|
|
put("sys/byte_order", W);
|
|
logprintf(logdepth+1, "OK (%s first)\n", W);
|
|
return 0;
|
|
}
|
|
|
|
for (index=0; endians1[index]!=NULL; ++index)
|
|
{
|
|
sprintf(test_c_blind, test_c_blind_template, endians1[index], endians1[index]);
|
|
|
|
if (compile_run(logdepth, test_c_blind, NULL, NULL, NULL, NULL) == 0)
|
|
{
|
|
W = endians2[index];
|
|
report("OK (%s first)\n", W);
|
|
put(key, W);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
report("FAILED (cannot determine byte order, you must supply it)\n");
|
|
return try_fail(logdepth, key);
|
|
}
|
|
|
|
static int test_shell_eats_backslash(int logdepth)
|
|
{
|
|
char *test = "echo c:\\n";
|
|
char *out;
|
|
|
|
|
|
logprintf(logdepth, "testing if shell eats \\...\n");
|
|
run_shell(logdepth+1, test, &out);
|
|
if (out == NULL) {
|
|
logprintf(logdepth+1, "oops, couldn't run shell?! (returned NULL)\n");
|
|
report("ERROR: shell fails.");
|
|
abort();
|
|
}
|
|
if (out[2] == '\\') {
|
|
logprintf(logdepth, "shell does NOT eat \\...\n");
|
|
put("sys/shell_eats_backslash", sfalse);
|
|
free(out);
|
|
return 0;
|
|
}
|
|
|
|
free(out);
|
|
logprintf(logdepth, "shell eats \\...\n");
|
|
put("sys/shell_eats_backslash", strue);
|
|
return 1;
|
|
}
|
|
|
|
|
|
static int try_get_cwd(int logdepth, const char *cmd)
|
|
{
|
|
char *cwd, *end;
|
|
run_shell(logdepth+1, cmd, &cwd);
|
|
if (cwd != NULL) {
|
|
end = strpbrk(cwd, "\r\n");
|
|
if (end != NULL)
|
|
*end = '\0';
|
|
if (*cwd != '\0') {
|
|
end = cwd + strlen(cwd) - 1;
|
|
while((*end == ' ') || (*end == '\t')) { *end = '\0'; end--; }
|
|
put("sys/tmp", cwd);
|
|
/* ugly hack for win32: paths there start as c:\ */
|
|
if ((cwd[1] == ':') && (cwd[2] == '\\'))
|
|
append("sys/tmp", "\\");
|
|
else
|
|
append("sys/tmp", "/");
|
|
logprintf(logdepth, "cwd is '%s'\n", get("sys/tmp"));
|
|
free(cwd);
|
|
return 1;
|
|
}
|
|
else
|
|
free(cwd);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int try_tmp(int logdepth)
|
|
{
|
|
char *fn;
|
|
|
|
logprintf(logdepth, "validating temp dir '%s'\n", get("sys/tmp"));
|
|
fn = tempfile_new_noabort("");
|
|
if (fn != NULL) {
|
|
unlink(fn);
|
|
free(fn);
|
|
logprintf(logdepth, "temp dir works!\n");
|
|
return 1;
|
|
}
|
|
|
|
logprintf(logdepth, "temp dir fails\n");
|
|
return 0;
|
|
}
|
|
|
|
/* test temp dir with all sort of workarounds */
|
|
static int try_tmp_all(int logdepth)
|
|
{
|
|
const char *tmp, *si;
|
|
char c;
|
|
char *t, *so, *old_tmp;
|
|
int eats, n;
|
|
|
|
tmp = get("sys/tmp");
|
|
|
|
/* path must end in path separator */
|
|
c = tmp[strlen(tmp)-1];
|
|
if ((c != '/') && (c != '\\')) {
|
|
append("sys/tmp", "/");
|
|
tmp = get("sys/tmp");
|
|
}
|
|
|
|
logprintf(logdepth, "trying detected temp dir '%s'\n", tmp);
|
|
if (try_tmp(logdepth+1)) return 1;
|
|
|
|
/* try msys-on-windows hack: if path starts with /d/something, try d:/ instead */
|
|
if ((tmp[0] == '/') && (isalpha(tmp[1])) && (tmp[2] == '/')) {
|
|
/* for the next test we shouldn't use our half-detected tmp path but go with . */
|
|
old_tmp = strclone(tmp);
|
|
put("sys/tmp", "");
|
|
eats = istrue(get("sys/shell_eats_backslash"));
|
|
tmp = old_tmp;
|
|
logprintf(logdepth, "tmp2='%s' eats=%d\n", tmp, eats);
|
|
t = malloc(strlen(tmp) * 2);
|
|
t[0] = tmp[1];
|
|
t[1] = ':';
|
|
for(si = tmp + 2, so = t + 2; *si != '\0'; si++, so++) {
|
|
if (*si == '/') {
|
|
*so = '\\';
|
|
if (eats) {
|
|
for(n = 0; n < 3; n++) {
|
|
so++;
|
|
*so = '\\';
|
|
}
|
|
}
|
|
}
|
|
else
|
|
*so = *si;
|
|
}
|
|
*so = '\0';
|
|
free(old_tmp);
|
|
|
|
logprintf(logdepth, "trying windows fix: '%s'\n", t);
|
|
put("sys/tmp", t);
|
|
free(t);
|
|
if (try_tmp(logdepth+1)) {
|
|
if (eats)
|
|
put("sys/path_sep", "\\\\\\\\");
|
|
else
|
|
put("sys/path_sep", "\\");
|
|
put("sys/path_sep_escaped", "\\\\");
|
|
return 1;
|
|
}
|
|
tmp = get("sys/tmp");
|
|
}
|
|
|
|
/* fail. Set back tmp to empty so next command has a chance to run */
|
|
put("sys/tmp", "");
|
|
return 0;
|
|
}
|
|
|
|
int find_tmp(const char *name, int logdepth, int fatal)
|
|
{
|
|
const char *usertmp;
|
|
|
|
if (in_cross_target) {
|
|
report("Temp dir for cross compilation target is the same as for host...");
|
|
logprintf(logdepth, "Copying temp dir from host to target\n");
|
|
require("/host/sys/tmp", logdepth, fatal);
|
|
usertmp = get("/host/sys/tmp");
|
|
if (usertmp == NULL) {
|
|
report("Host temp dir not found.\n");
|
|
logprintf(logdepth, "Host temp dir not found.\n");
|
|
return 1;
|
|
}
|
|
put("sys/tmp", usertmp);
|
|
return 0;
|
|
}
|
|
|
|
/* we need shell for later tests; do this detection in . */
|
|
put("sys/tmp", "");
|
|
require("sys/shell", logdepth, fatal);
|
|
|
|
put("sys/path_sep", "/");
|
|
put("sys/path_sep_escaped", "/");
|
|
|
|
report("Detecting temp dir...");
|
|
logprintf(logdepth, "Finding temp dir (current working directory)...\n");
|
|
|
|
usertmp = get("/arg/sys/tmp");
|
|
|
|
/* . as tmp would fail for commands including a "cd" - this would cause
|
|
temporary files left in the target dir. We start out with empty
|
|
string (which is ., but on windows ./ would fail), and run
|
|
pwd (without cd) to find out the current directory (as getcwd() is not
|
|
portable). If pwd fails, we stay with ./ */
|
|
put("sys/tmp", "");
|
|
|
|
/* we need to know about shell backslash problem regardless of how
|
|
we end up with tmp - currently tmp is ., where the test could run
|
|
safely */
|
|
test_shell_eats_backslash(logdepth+1);
|
|
|
|
/* Special case: cross-compilation with emulator; we can not assume
|
|
the emulator uses the same paths as the host system, while we mix
|
|
accessing files from host and emu. If we stay in ., both emulator
|
|
and host system should be (more or less) happy. */
|
|
if (istarget(db_cwd) && iscross) {
|
|
if (usertmp == NULL) {
|
|
report("using temp dir . for cross-compilation\n");
|
|
logprintf(logdepth, "staying with . for cross-compilation\n");
|
|
}
|
|
else {
|
|
put("sys/tmp", usertmp);
|
|
report("using user supplied temp dir '%s' for cross-compilation\n", usertmp);
|
|
logprintf(logdepth, "using user supplied temp dir '%s' for cross-compilation\n", usertmp);
|
|
logprintf(logdepth, "Path sep: '%s'\n", get("sys/path_sep"));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if ((usertmp != NULL))
|
|
put("sys/tmp", usertmp);
|
|
|
|
if (
|
|
((usertmp != NULL) && (try_tmp_all(logdepth+2))) || /* try user supplied temp dir */
|
|
((try_get_cwd(logdepth+1, "pwd")) && (try_tmp_all(logdepth+2))) || /* try pwd for finding out cwd */
|
|
((try_get_cwd(logdepth+1, "echo %cd%") && (try_tmp_all(logdepth+2))))) { /* try windows-specific way for finding out cwd */
|
|
|
|
report(" validated %s\n", get("sys/tmp"));
|
|
logprintf(logdepth, "Detected temp dir '%s'\n", get("sys/tmp"));
|
|
logprintf(logdepth, "Path sep: '%s'\n", get("sys/path_sep"));
|
|
return 0;
|
|
}
|
|
|
|
put("sys/tmp", "");
|
|
report("using temp dir fallback .\n");
|
|
logprintf(logdepth, "all temp directories failed, using . as tmp\n");
|
|
logprintf(logdepth, "Path sep: '%s'\n", get("sys/path_sep"));
|
|
return 0;
|
|
}
|
|
|
|
int test_shell(const char *shell, int logdepth, int quote)
|
|
{
|
|
char *test = "echo hello";
|
|
char *cmd;
|
|
char *out;
|
|
char *q;
|
|
|
|
if (quote)
|
|
q = "\"";
|
|
else
|
|
q = "";
|
|
|
|
logprintf(logdepth, "testing '%s' as shell\n", shell);
|
|
cmd = malloc(strlen(test) + strlen(shell) + 8);
|
|
sprintf(cmd, "%s %s%s%s", shell, q, test, q);
|
|
|
|
run(logdepth+1, cmd, &out);
|
|
|
|
free(cmd);
|
|
|
|
if ((out != NULL) && (strncmp(out, "hello", 5) == 0)) {
|
|
put("sys/shell", shell);
|
|
if (quote)
|
|
put("sys/shell_needs_quote", strue);
|
|
else
|
|
put("sys/shell_needs_quote", sfalse);
|
|
logprintf(logdepth, "accepted.\n");
|
|
free(out);
|
|
return 1;
|
|
}
|
|
|
|
logprintf(logdepth, "refused.\n");
|
|
free(out);
|
|
return 0;
|
|
}
|
|
|
|
static int find_shell_escape(const char *name, int logdepth, int fatal, const char *shell)
|
|
{
|
|
char cmdline[256];
|
|
char **t;
|
|
char *tests[] = {
|
|
"\\", "\\ {}&;|",
|
|
"^", "^ &",
|
|
NULL, NULL
|
|
};
|
|
(void) fatal; /* not used */
|
|
(void) shell; /* not used */
|
|
|
|
report("Looking for a shell escape character... ");
|
|
logprintf(logdepth, "finding shell escape character...\n");
|
|
|
|
for(t = tests; *t != NULL; t += 2) {
|
|
char *s, *end, *out, *start;
|
|
strcpy(cmdline, "echo ");
|
|
end = cmdline+5;
|
|
for(s = t[1]; *s != '\0'; s++) {
|
|
*end++ = *t[0];
|
|
*end++ = *s;
|
|
}
|
|
*end = '\0';
|
|
run(logdepth+1, cmdline, &out);
|
|
if (out != NULL) {
|
|
int res;
|
|
if (*out == '\"') /* wine likes to wrap the output in quotes for some reason */
|
|
start = out+1;
|
|
else
|
|
start = out;
|
|
|
|
res = strncmp(start, t[1], strlen(t[1]));
|
|
free(out);
|
|
if (res == 0) {
|
|
report("found: '%s'\n", t[0]);
|
|
logprintf(logdepth, "found shell escape char '%s'\n", t[0]);
|
|
put("sys/shell_escape_char", t[0]);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
report("NOT FOUND\n");
|
|
logprintf(logdepth, "shell escape character not found\n");
|
|
|
|
return 1;
|
|
}
|
|
|
|
int find_shell(const char *name, int logdepth, int fatal)
|
|
{
|
|
const char *shells[] = {
|
|
"/bin/sh -c",
|
|
"/bin/bash -c",
|
|
"bash -c",
|
|
"cmd.exe /c",
|
|
"sh -c",
|
|
"/bin/dash -c",
|
|
"dash -c",
|
|
"/bin/ksh -c",
|
|
"ksh -c",
|
|
NULL
|
|
};
|
|
const char **s;
|
|
|
|
if (cross_blind) {
|
|
const char *shell = get("/arg/sys/target-shell");
|
|
if (shell == NULL) {
|
|
report("Need to specify sys/target-shell in blind cross compiling mode, because the shell cannot be detected (note: scconfig will not attempt to run the target shell)\n");
|
|
exit(1);
|
|
}
|
|
|
|
put("sys/shell", shell);
|
|
report("Blind cross compiling: accepting '%s' as shell\n", shell);
|
|
logprintf(logdepth, "Blind cross compiling: accepting '%s' as shell\n", shell);
|
|
return 0;
|
|
}
|
|
|
|
report("Looking for a shell... ");
|
|
logprintf(logdepth, "finding a shell\n");
|
|
|
|
for(s = shells; *s != NULL; s++) {
|
|
if ((test_shell(*s, logdepth+1, 0)) || (test_shell(*s, logdepth+1, 1))) {
|
|
report("%s\n", *s);
|
|
logprintf(logdepth, "found a shell '%s', need quote: %s\n", *s, get("sys/shell_needs_quote"));
|
|
return find_shell_escape(name, logdepth, fatal, *s);
|
|
}
|
|
}
|
|
|
|
report("NOT FOUND\n");
|
|
logprintf(logdepth, "shell not found\n");
|
|
return 1;
|
|
}
|