Go to:
Gentoo Home
Documentation
Forums
Lists
Bugs
Planet
Store
Wiki
Get Gentoo!
Gentoo's Bugzilla – Attachment 10899 Details for
Bug 18933
next generation gcc-config
Home
|
New
–
[Ex]
|
Browse
|
Search
|
Privacy Policy
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
wrapper-1.4.3.c
wrapper-1.4.3.c (text/plain), 17.76 KB, created by
Zach Welch (RETIRED)
on 2003-04-20 02:33:46 UTC
(
hide
)
Description:
wrapper-1.4.3.c
Filename:
MIME Type:
Creator:
Zach Welch (RETIRED)
Created:
2003-04-20 02:33:46 UTC
Size:
17.76 KB
patch
obsolete
>/* > * Copyright 1999-2003 Gentoo Technologies, Inc. > * Distributed under the terms of the GNU General Public License v2 > * Author: Martin Schlemmer <azarah@gentoo.org> > * $Header: /home/cvsroot/gentoo-x86/sys-devel/gcc-config/files/wrapper-1.4.1.c,v 1.1 2003/04/12 18:44:22 azarah Exp $ > */ > >#define _REENTRANT >#define _GNU_SOURCE > >#include <stdio.h> >#include <stdlib.h> >#include <sys/types.h> >#include <sys/stat.h> >#include <sys/param.h> >#include <unistd.h> >#include <wait.h> >#include <libgen.h> >#include <string.h> >#include <stdarg.h> >#include <errno.h> > >#define BIN_PATH "/usr/bin" > >/* common definitions -- should be configurable */ >#define GCC_CONFIG "/usr/bin/gcc-config" > >/* ENVD_PATH is the location of gcc-config's configuration files */ >#define ENVD_PATH "/etc/env.d/gcc" > >/* HOME_PATH is the relative path in each users $HOME directory; by > default, this creates a subdirectory for all the configuration files */ >#define HOME_PATH ".gcc-config" > >/* CONFIG_FILE is the base name for config files; with non-native > toolchains, this will be suffixed with "-${CHOST}" */ >#define CONFIG_FILE "config" > >/* ENVD_FILE is the name of the fallback env file (relative to ENVD_PATH */ >#define ENVD_FILE "../05gcc" > > >struct wrapper_data { > /* our inputs */ > char *chost; /* determined via argv[0], CBUILD, or CHOST */ > char *name; /* munged basename of argv[0] */ > char *fullname; /* /usr/bin/${name} */ > > /* our output */ > char *bin; /* holds fully qualified name of the "real" binary */ >}; > >/* ============================================================== */ > >static void wrapper_exit(char *msg, ...) >{ > va_list args; > va_start(args, msg); > vfprintf(stderr, msg, args); > va_end(args); > exit(1); >} > >static void *xmalloc(size_t len) { > void *x = malloc(len); > if (NULL == x) > wrapper_exit("gcc-config wrapper: unable to allocate memory\n"); > return x; >} > >static char *xstrdup(const char *str) { > void *x = strdup(str); > if (NULL == x) > wrapper_exit("gcc-config wrapper: unable to allocate string\n"); > return x; >} > >static char *join_filename(char *dir, char *name) >{ > char *str; > size_t len; > len = strlen(dir) + 1 + strlen(name) + 1; > if (len > MAXPATHLEN) > wrapper_exit("%s wrapper: path too long", name); > str = xmalloc(len); > sprintf(str, "%s/%s", dir, name); > return str; >} > >static const char *wrapper_strerror(int err) >{ > char *str = xmalloc(MAXPATHLEN); > strerror_r(err, str, sizeof(str)); > return str; >} > >/* ============================================================== */ > > >/* strip_shell_stuff cleanses a parameter set in a bash assignment > returns a portion of passed string, possibly shortened */ >static char* strip_shell_stuff(char *str) >{ > size_t len = strlen(str), last; > > /* remove possible trailing newline */ > if ((0 != len) && ('\n' == str[len - 1])) > len -= 1; > > /* strip matching pairs of quotes */ > last = len - 1; > while ( (len > 2) && > ((('\'' == str[0]) && ('\'' == str[last])) || > (('\"' == str[0]) && ('\"' == str[last]))) ) > { > str++; > len -= 2; > last = len - 1; > } > > /* now (re)terminate string at (possibly shorter) len */ > str[len] = 0; > return str; >} > >/* ============================================================== */ > >static void wrapper_setenv(char *str, char* value) { > if (-1 == setenv(str, value, 1)) > wrapper_exit("wrapper: unable to update environment"); >} > >/* wrapper_tokenize works like strtok but takes a function pointer that > allows each token to be parsed differently. It also only works > on colon delimited strings. */ >static int wrapper_tokenize(char *str, > int (*func)(char *token, void *data), void *data) >{ > char *s; > char *state; > char *token; > int result; > > s = xstrdup(str); > token = strtok_r(s, ":", &state); > do { > result = (*func)(token, data); > if (0 != result) break; > } while (NULL != (token = strtok_r(NULL, ":", &state))); > free(s); > > return result; >} > >/* find_str_in_list searches a colon delimted list for an > occurance of str; returns 1 if found, 0 if not. > find_str_parse_callback is called for each item in list */ >static int find_str_token_callback(char *token, void *data) { > return (0 == strcmp(token, (char*)data)); >} >static int find_str_in_list(char *str, char* list) { > wrapper_tokenize(list, find_str_token_callback, str); > return 0; >} > >/* need_to_prepend returns 1 if value is appropriately located in > the envval; if atstart is 1, value *must* appear at the very > beginning of the list; otherwise, it can be anywhere */ >static int need_to_prepend(char *envval, char *value, int atstart) >{ > int result; > if (atstart) { > /* envval may already have value first */ > size_t len = strlen(value); > result = (0 != strncmp(envval, value, len)) || > ((':' != envval[len]) && (0 != envval[len])); > } > else { > /* it can be anywhere -- hmmmm, what would use this? */ > result = !find_str_in_list(value, envval); > } > return result; >} > >/* prepend_env_value adds the specified env value prepended to > the appropriate env var */ >static void prepend_env_value(char *key, char* value, int atstart) >{ > /* yah, see, tha thing is... */ > char *envval; > char *newval; > > envval = getenv(key); > if (NULL == envval) { > wrapper_setenv(key, value); > return; > } > > /* only prepend the value if required */ > if (need_to_prepend(envval, value, atstart)) { > /* create new env string and store it in env */ > newval = xmalloc(strlen(value) + 1 + strlen(envval) + 1); > sprintf(newval, "%s:%s", value, envval); > wrapper_setenv(key, newval); > free(newval); > } >} > >/* check_config_line does basic sanity checks on lines read from > configuration files. This avoids errors and extra processing. >*/ >static int check_config_line(char *str, char *file, int line) >{ > size_t len = strlen(str); > > if ((MAXPATHLEN == len) && ('\n' != str[len - 1])) > wrapper_exit("%s: line %d is too long", file, line); > > if ('#' == str[0]) > return 0; > > return 1; >} > >/* get_config_value returns the value of a configuration setting, > this is used to read 1) the chost configuration file, which > specifies 2) the chost environment file, which has the bin path > Note: if prefix is NULL, all env vars are set into active env */ >static char *get_config_value(char *prefix, char *name) >{ > FILE *file = NULL; > char *value = NULL; > char *path = NULL; > char *str; > size_t len; > int line = 1; > > str = xmalloc(MAXPATHLEN + 1); > > file = fopen(name, "r"); > if (NULL == file) return 0; > > if (NULL != prefix) > len = strlen(prefix); > > while (NULL != fgets(str, MAXPATHLEN, file)) { > /* check for errors and skip over useless stuff */ > if (!check_config_line(str, name, line++)) continue; > > value = strchr(str, '='); > if (NULL == value) { > fprintf(stderr, "%s:%d: unexpected input: %s\n", > name, line, str); > continue; > } > *value++ = 0; > > if (NULL != prefix) { > if (0 == strcmp(prefix, str)) > break; > } > else { > /* set up everthing, but only vars that matter */ > if (0 == strcmp(str, "PATH")) { > path = xstrdup(value); > } > else if (0 == strcmp(str, "LDPATH")) { > prepend_env_value(str, > strip_shell_stuff(value), > 1); > } > else if (0 == strcmp(str, "STDCXX_INCDIR")) > wrapper_setenv(str, strip_shell_stuff(value)); > } > } > fclose(file); > > if (NULL != path) { > strcpy(str, path); > value = str; > free(path); > } > /* if we have a value, strip it and dup it for return */ > if (NULL != value) > value = xstrdup(strip_shell_stuff(value)); > > free(str); > > return value; >} >/* ============================================================== */ > >static char* check_config_file(char *path, char *conffile) >{ > char *confname; > struct stat s; > int result; > > confname = join_filename(path, conffile); > result = access(confname, R_OK | F_OK); > if (0 == result) { > result = stat(confname, &s); > if ((0 == result) && !(s.st_mode & S_IFREG)) > result = -1; > } > > if (0 != result) { > free(confname); > confname = NULL; > } > > return confname; >} > >/* make_chost_config_name returns "config" or "config-${CHOST}" (today) */ >static char* make_chost_config_name(char *chost) >{ > char *confname; > > if (NULL != chost) { > confname = xmalloc(strlen(CONFIG_FILE) + 1 + strlen(chost) + 1); > sprintf(confname, "%s-%s", CONFIG_FILE, chost); > } > else > confname = xstrdup(CONFIG_FILE); > > return confname; >} > > >/* check_home_config_file tries to access the user's own config file, > returning its full path name if successful*/ >static char *check_home_config_file(char *conffile) >{ > char *confname = NULL; > char *home; > > home = getenv("HOME"); > if (NULL != home) { > char *confdir = join_filename(home, HOME_PATH); > confname = check_config_file(confdir, conffile); > free(confdir); > } > > return confname; >} > >/* get_chost_config_filename returns the name of the gcc-config > configuration file for the specified chost. This file contains > the "CURRENT" setting, which specifies the active envd file. > The search order for this is the following: > 1) look for a profile matching chost in GCCC_PROFILES > */ >static char *get_chost_config_filename(char *chost) >{ > char *conffile; > char *confname; > > /* 2) look for $HOME/.gcc-config/<conffile> */ > conffile = make_chost_config_name(chost); > confname = check_home_config_file(conffile); > > /* 3) if not found, look for /etc/env.d/gcc/<conffile> */ > if (NULL == confname) > confname = check_config_file(ENVD_PATH, conffile); > > free(conffile); > > return confname; >} > >/* find_env_profile_* are used to locate the CURRENT spec for chost > from the GCCC_PROFILES list. */ >struct find_env_profile_data { > char *profile; > char *chost; > size_t chostlen; >}; >static int find_env_profile_callback(char *token, void *data) >{ > struct find_env_profile_data *pd = data; > if (0 == strncmp(token, pd->chost, pd->chostlen)) { > pd->profile = xstrdup(token); > return 1; > } > return 0; >} >static char *find_env_profile_name(char *chost, char *profiles) >{ > struct find_env_profile_data *pd; > char *conffile; > > pd = xmalloc(sizeof(*pd)); > pd->chost = chost; > pd->chostlen = strlen(chost); > pd->profile = NULL; > > (void)wrapper_tokenize(profiles, find_env_profile_callback, pd); > > conffile = pd->profile; > free(pd); > > return conffile; >} > >/* get_envd_config_filename returns the name of the active configuration > configuration file for a given chost. It reads the config-$CHOST file > to extract the CURRENT setting. */ >static char* get_envd_config_filename(char *chost) >{ > char *specfile = NULL; > char *specname; > > /* if we don't know our CHOST, we can't use GCCC_PROFILES! */ > if (NULL != chost) { > /* look for CURRENT profile spec in GCCC_PROFILES */ > char *gccc_profiles = getenv("GCCC_PROFILES"); > if (NULL != gccc_profiles) > specfile = find_env_profile_name(chost, gccc_profiles); > } > > if (NULL == specfile) { > /* find the right config file and read the profile there */ > char *confname = get_chost_config_filename(chost); > specfile = get_config_value("CURRENT", confname); > free(confname); > if (NULL == specfile) return NULL; > } > > /* now create a copy of a name to return */ > specname = join_filename(ENVD_PATH, specfile); > free(specfile); > > return specname; >} > >static char *get_path_from_envd(char *chost) >{ > char *envname; > char *envpath; > > /* search the appropriate environment file to search for a PATH */ > envname = get_envd_config_filename(chost); > if (NULL == envname) return NULL; >#if 0 > /* only set PATH for native compiiles (no!) */ > envpath = get_config_value("PATH", envname); >#else > envpath = get_config_value(NULL, envname); >#endif > free(envname); > > return envpath; >} > >/* ============================================================== */ > >/* check_for_target checks in dir for the file we are seeking > * it returns 1 if found (with data->bin setup), 0 if not and > * negative on error > */ >static char *check_for_target(char *dir, char *name, char *fullname) >{ > char *str; > struct stat sbuf; > int result; > > /* Stat possible file to check that > * 1) it exist and is a regular file, and > * 2) it is not the wrapper itself, and > * 3) it is in a /gcc-bin/ directory tree > */ > str = join_filename(dir, name); > result = stat(str, &sbuf); > if ((0 != result) || > !(sbuf.st_mode & S_IFREG) || > (0 == strcmp(str, fullname)) || > (NULL == strstr(str, "/gcc-bin/"))) { > free(str); > str = NULL; > } > > return str; >} > >/* find_target_in_envd uses gcc-config's configuration files to > find the correct binary path directly. This now also works > to locate the appropriate cross-compilers as well. */ >static int find_target_in_envd(struct wrapper_data *data) >{ > char *str; > char *envpath; > > /* if we find a PATH, ensure that it's actually there */ > str = xmalloc(MAXPATHLEN); > envpath = get_path_from_envd(data->chost); > if (NULL != envpath) { > data->bin = check_for_target(envpath, > data->name, data->fullname); > } > free(str); > return NULL != data->bin; >} > >/* find_target_in_path tries to find our target in PATH */ >static int find_target_token_callback(char *token, void *xdata) { > struct wrapper_data *data = xdata; > if (0 != strlen(token)) return 0; > data->bin = check_for_target(token, data->name, data->fullname); > return NULL != data->bin; >} >static int find_target_in_path(struct wrapper_data *data) >{ > char *path; > /* sometimes, PATH may not be set (silly user-errors anyway) */ > path = getenv("PATH"); > if (NULL == path) return 0; > return wrapper_tokenize(path, find_target_token_callback, data); >} > >/* find_target_oldschool -- with the above enhancements, I question > whether or not the remaining code is useful anymore. (DEPRECATED) */ >static int find_target_oldschool(struct wrapper_data *data) >{ > FILE *inpipe; > char *str; > > /* find_target_in_envd now provides the same functionality > that gcc-config --get-bin-path used to provide */ > fprintf(stderr, > "%s: warning: calling gcc-config to find target path\n", > data->name); > > /* Only our wrapper is in PATH, so get the CC path using > gcc-config and execute the real binary in there... */ > str = xmalloc(MAXPATHLEN); > sprintf(str, "%s --get-bin-path", GCC_CONFIG); > if (NULL != data->chost) { > strcat(str, " "); > if (strlen(str) + strlen(data->chost) > MAXPATHLEN) { > wrapper_exit("%s wrapper: invalid chost: %s\n", > data->name, data->chost); > } > strcat(str, data->chost); > } > > inpipe = popen(str, "r"); > if (NULL == inpipe) { > wrapper_exit( > "%s wrapper: Could not open pipe for gcc-config: %s\n", > data->name, wrapper_strerror(errno)); > } > if (0 == fgets(str, MAXPATHLEN, inpipe)) { > wrapper_exit( > "%s wrapper: Could not get compiler binary path: %s\n", > wrapper_strerror(errno)); > } > pclose(inpipe); > > data->bin = check_for_target(str, data->name, data->fullname); > > free(str); > > return NULL != data->bin; >} > >static void find_wrapper_target(struct wrapper_data *data) >{ > /* Find the first file with suitable name in PATH. The idea here is > * that we do not want to bind ourselfs to something static like the > * default profile, or some odd environment variable, but want to be > * able to build something with a non default gcc by just tweaking > * the PATH. We skip this for cross-compiling... hmmm > */ > if ((NULL == data->chost) && find_target_in_path(data)) > return; > > /* If we are cross-compiling or the PATH has been dorked up, > * then we are forced to use using gcc-config's files directly > */ > if (find_target_in_envd(data)) > return; > > /* If we make it here, we're probably dead in the water. But > * we might as well make one last feeble attempt to call > * gcc-config to read the same files we just tried to get at. > * If this is happening, this is a BIG FAT BUG!!! > */ > if (find_target_oldschool(data)) > return; > > wrapper_exit("%s wrapper: Could not find compiler binary path\n", > data->name); >} > >/* ============================================================== */ > >/* This function modifies PATH to have gcc's bin path prepended */ >static void modify_path(struct wrapper_data *data) >{ > char *str; > char *dname; > > /* save a copy of the target binary location, and get > its directory component */ > str = xstrdup(data->bin); > dname = dirname(str); > if (NULL == dname) return; > prepend_env_value("PATH", dname, 1); > > free(str); > > return; >} > >/* ============================================================== */ > >/* guess_chost_from_env is used when the wrapper is called in an > uncanonicalized form (e.g. 'gcc') to determine the compiler host > if we can figure this out, we can avoid opening and reading the > config-HOST file if GCCC_PROFILES is set*/ >char *guess_chost_from_env() >{ > char *cbuild; > char *chost; > > cbuild = getenv("CBUILD"); > chost = getenv("CHOST"); > if ((NULL == cbuild) || (NULL == chost) || > (0 == strcmp(cbuild, chost))) > return NULL; > > return NULL; /*xstrdup(cbuild);*/ >} > >void capture_chost_name(char *argv0, struct wrapper_data *data) >{ > size_t len = 0; > char *cp; > > cp = xstrdup(argv0); > data->name = xstrdup(basename(cp)); > free(cp); > > cp = strrchr(data->name, '-'); > if (NULL != cp) { > len = cp - data->name; > if (0 == len) > wrapper_exit("gcc-config wrapper: unknown link: %s\n", > argv0); > data->chost = xstrdup(data->name); > data->chost[len++] = 0; > } > else > data->chost = guess_chost_from_env(); > > /* fixup cc calls -- i feel so dirty for doing this... */ > if (0 == strcmp(data->name + len, "cc")) > strcpy(data->name + len, "gcc"); > > /* What is the full name of our wrapper? */ > data->fullname = join_filename(BIN_PATH, data->name); >} > >int main(int argc, char **argv) >{ > struct wrapper_data *data; > char *bin; > > data = alloca(sizeof(*data)); > if (NULL == data) > wrapper_exit("%s wrapper: out of memory\n", argv[0]); > memset(data, 0, sizeof(*data)); > > /* the program name tells us what to do, > so save it (and chost prefix) */ > capture_chost_name(argv[0], data); > > /* okay, now find the wrapper target */ > find_wrapper_target(data); > > /* then prepend its path to PATH, if not already there */ > modify_path(data); > > bin = alloca(strlen(data->bin) + 1); > strcpy(bin, data->bin); > > free(data->chost); > free(data->fullname); > free(data->name); > free(data->bin); > > /* Set argv[0] to the correct binary, else gcc do not find internal > * headers, etc (bug #8132). Then Just Do It */ > argv[0] = bin; > if (execv(bin, argv) < 0) > wrapper_exit("Could not run/locate \"%s\"\n", data->name); > > return 0; >} >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Raw
Actions:
View
Attachments on
bug 18933
:
10573
|
10898
|
10899
|
10900