int
main(int argc, char *argv[])
{
    char *program_name, *compiler_name, *other_wrappers, *sysroot;
    struct args *compiler_args, *cmd;

    compiler_args = args_init(argc, argv);

    /* check if we were called directly. */
    program_name = basename(compiler_args->argv[0]);
    args_remove_first(compiler_args);
    if (str_eq(program_name, MYNAME)) {
        /* the first argument should be a compiler */
        if (compiler_args->argc < 1 || compiler_args->argv[0][0] == '-') {
            fprintf(stderr, "%s", USAGE_TEXT);
            exit(1);
        }

        compiler_name = x_strdup(compiler_args->argv[0]);
        args_remove_first(compiler_args);
    } else {
        compiler_name = x_strdup(program_name);
    }
    free(program_name);

    other_wrappers = getenv(MYNAME "_OTHER_WRAPPERS");
    if (!other_wrappers) {
        other_wrappers = "ccache distcc";
    }

    cmd = find_all_executables(compiler_name, MYNAME, other_wrappers);
    if (!cmd) {
        fatal("No valid executables named %s found!", compiler_name);
    }
    free(compiler_name);

    sysroot = getenv("SYSROOT");
    if (sysroot) {
        args_add(cmd, "--sysroot");
        args_add(cmd, sysroot);
    }

    args_extend(cmd, compiler_args);
    args_free(compiler_args);

    execute(cmd->argv);
    return 1;
}
Ejemplo n.º 2
0
Archivo: args.c Proyecto: OPSF/uClinux
/* does the same as args_add(), but if the string s contains spaces, splits it into separate args 
 * deals with spaces at the beginning and end */
void args_add_with_spaces(ARGS *args, const char *s)
{
	char *tmp_arg;
	int i;
	int arg_index = 0;
	int space = 0;
	int text = 0;

	for (i = 0; i <= strlen(s); i++) {
		if (s[i] == ' ' || s[i] == '\0') {
			if (text) {
				/* end of arg */
				tmp_arg = strndup(&s[arg_index], i - arg_index);
				if (tmp_arg) {
					args_add(args, tmp_arg);
					free (tmp_arg);
				} else {
					perror("strndup");
					exit(1);
				}
			}
			text = 0;
			space = 1;
		} else {
			if (space) {
				/* new arg */
				arg_index = i;
			}
			text = 1;
			space = 0;
		}
	}
}
Ejemplo n.º 3
0
Archivo: args.c Proyecto: ccache/ccache
// Add all arguments in to_append to args.
void
args_extend(struct args *args, struct args *to_append)
{
	for (int i = 0; i < to_append->argc; i++) {
		args_add(args, to_append->argv[i]);
	}
}
Ejemplo n.º 4
0
Archivo: args.c Proyecto: ccache/ccache
struct args *
args_init(int init_argc, char **init_args)
{
	struct args *args = (struct args *)x_malloc(sizeof(struct args));
	args->argc = 0;
	args->argv = (char **)x_malloc(sizeof(char *));
	args->argv[0] = NULL;
	for (int i = 0; i < init_argc; i++) {
		args_add(args, init_args[i]);
	}
	return args;
}
Ejemplo n.º 5
0
Archivo: args.c Proyecto: OPSF/uClinux
ARGS *args_init(int init_argc, char **init_args)
{
	ARGS *args;
	int i;
	args = (ARGS *)malloc(sizeof(ARGS));
	args->argc = 0;
	args->argv = (char **)malloc(sizeof(char *));
	args->argv[0] = NULL;
	for (i=0;i<init_argc;i++) {
		args_add(args, init_args[i]);
	}
	return args;
}
Ejemplo n.º 6
0
Archivo: args.c Proyecto: ccache/ccache
struct args *
args_init_from_string(const char *command)
{
	char *p = x_strdup(command);
	char *q = p;
	char *word, *saveptr = NULL;
	struct args *args = args_init(0, NULL);
	while ((word = strtok_r(q, " \t\r\n", &saveptr))) {
		args_add(args, word);
		q = NULL;
	}

	free(p);
	return args;
}
Ejemplo n.º 7
0
/*
 * Process the compiler options into options suitable for passing to the
 * preprocessor and the real compiler. The preprocessor options don't include
 * -E; this is added later. Returns true on success, otherwise false.
 */
bool
armcc_process_args(struct args *orig_args, struct args **preprocessor_args,
                struct args **compiler_args)
{
	int i;
	bool found_c_opt = false;
	bool found_S_opt = false;
	bool found_pch = false;
	bool found_fpch_preprocess = false;
	const char *actual_language;          /* Language to actually use. */
	const char *input_charset = NULL;
	struct stat st;
	/* is the dependency makefile name overridden with --depend? */
	bool dependency_filename_specified = false;
	/* is the dependency makefile target name specified ? */
	bool dependency_target_specified = false;
	char *dep_file = NULL, *dep_dir = NULL;
	struct args *stripped_args = NULL, *dep_args = NULL;
	int argc = orig_args->argc;
	char **argv = orig_args->argv;
	bool result = true;
	/* 0: Choose preprocessor type by the file extension.
	 * 1: Use c preprocessor.
	 * 2: Use c++ preprocessor.*/
	unsigned force_preprocessor_type = 0;

	stripped_args = args_init(0, NULL);
	dep_args = args_init(0, NULL);

	args_add(stripped_args, argv[0]);

	for (i = 1; i < argc; i++) {
		/* The user knows best: just swallow the next arg */
		if (str_eq(argv[i], "--ccache-skip")) {
			i++;
			if (i == argc) {
				cc_log("--ccache-skip lacks an argument");
				result = false;
				goto out;
			}
			args_add(stripped_args, argv[i]);
			continue;
		}

		/* Special case for -E. */
		if (str_eq(argv[i], "-E")) {
			stats_update(STATS_PREPROCESSING);
			result = false;
			goto out;
		}

		/* These are always too hard. */
		if (compopt_too_hard(argv[i])
		    || str_startswith(argv[i], "@")
		    || str_startswith(argv[i], "-fdump-")) {
			cc_log("Compiler option %s is unsupported", argv[i]);
			stats_update(STATS_UNSUPPORTED);
			result = false;
			goto out;
		}

		/* These are too hard in direct mode. */
		if (enable_direct) {
			if (compopt_too_hard_for_direct_mode(argv[i])) {
				cc_log("Unsupported compiler option for direct mode: %s", argv[i]);
				enable_direct = false;
			}
		}

		/* we must have -c */
		if (str_eq(argv[i], "-c")) {
			args_add(stripped_args, argv[i]);
			found_c_opt = true;
			continue;
		}

		/* -S changes the default extension */
		if (str_eq(argv[i], "-S")) {
			args_add(stripped_args, argv[i]);
			found_S_opt = true;
			continue;
		}

		/* we need to work out where the output was meant to go */
		if (str_eq(argv[i], "-o")) {
			if (i == argc-1) {
				cc_log("Missing argument to %s", argv[i]);
				stats_update(STATS_ARGS);
				result = false;
				goto out;
			}
			output_obj = argv[i+1];
			i++;
			continue;
		}

		/* alternate form of -o, with no space */
		if (str_startswith(argv[i], "-o")) {
			output_obj = &argv[i][2];
			continue;
		}


		/* If multiple source type options are there, the armcc will use the last one. */
		if (str_eq(argv[i], "--cpp")) { 
			force_preprocessor_type = 2;
			continue;
		}
		else if (str_eq(argv[i], "--c90") || str_eq(argv[i], "--c99")) { 
			force_preprocessor_type = 1;
			continue;
		}

		if (str_eq(argv[i], "--md")) { 
			generating_dependencies = true;
			continue;
		}

		/* The rvct started supporting --depend_target from 4.0.
		 * And there is a bug when using -E and --depend together with rvct which version is earlier than 4.0_697.
		 * That is too hard to support "--depend" for the earlier version of rvct.*/
		if (str_startswith(argv[i], "--depend_dir")) {
			/* We just concat the dir and the filename and pass the result 
			 * to --depend. */
			if (i >= argc - 1) {
				cc_log("Missing argument to %s", argv[i]);
				stats_update(STATS_ARGS);
				result = false;
				goto out;
			}
			dep_dir= x_strdup(argv[i+1]);
			i++;
			continue;
		}
		else if (str_startswith(argv[i], "--depend_target")) {
			if (i >= argc - 1) {
				cc_log("Missing argument to %s", argv[i]);
				stats_update(STATS_ARGS);
				result = false;
				goto out;
			}
			dependency_target_specified = true;
			args_add(dep_args, argv[i]);
			args_add(dep_args, argv[i+1]);
			i++;
			continue;
		}
		else if (str_startswith(argv[i], "--depend")) {
			dependency_filename_specified = true;
			generating_dependencies = true;

			if (i >= argc - 1) {
				cc_log("Missing argument to %s", argv[i]);
				stats_update(STATS_ARGS);
				result = false;
				goto out;
			}
			dep_file = x_strdup(argv[i + 1]);
			i++;
			continue;
		}


		/*
		 * Options taking an argument that that we may want to rewrite
		 * to relative paths to get better hit rate. A secondary effect
		 * is that paths in the standard error output produced by the
		 * compiler will be normalized.
		 */
		if (compopt_takes_path(argv[i])) {
			char *relpath;
			char *pchpath;
			if (i == argc-1) {
				cc_log("Missing argument to %s", argv[i]);
				stats_update(STATS_ARGS);
				result = false;
				goto out;
			}

			args_add(stripped_args, argv[i]);
			relpath = make_relative_path(x_strdup(argv[i+1]));
			args_add(stripped_args, relpath);

			/* Try to be smart about detecting precompiled headers */
			pchpath = format("%s.gch", argv[i+1]);
			if (stat(pchpath, &st) == 0) {
				cc_log("Detected use of precompiled header: %s", pchpath);
				found_pch = true;
			}

			free(pchpath);
			free(relpath);
			i++;
			continue;
		}

		/* Same as above but options with concatenated argument. */
		if (compopt_short(compopt_takes_path, argv[i])) {
			char *relpath;
			char *option;
			relpath = make_relative_path(x_strdup(argv[i] + 2));
			option = format("-%c%s", argv[i][1], relpath);
			args_add(stripped_args, option);
			free(relpath);
			free(option);
			continue;
		}

		/* options that take an argument */
		if (compopt_takes_arg(argv[i])) {
			if (i == argc-1) {
				cc_log("Missing argument to %s", argv[i]);
				stats_update(STATS_ARGS);
				result = false;
				goto out;
			}
			args_add(stripped_args, argv[i]);
			args_add(stripped_args, argv[i+1]);
			i++;
			continue;
		}

		/* other options */
		if (argv[i][0] == '-') {
			args_add(stripped_args, argv[i]);
			continue;
		}

		/* if an argument isn't a plain file then assume its
		   an option, not an input file. This allows us to
		   cope better with unusual compiler options */
		if (stat(argv[i], &st) != 0 || !S_ISREG(st.st_mode)) {
			cc_log("%s is not a regular file, not considering as input file",
			       argv[i]);
			args_add(stripped_args, argv[i]);
			continue;
		}

		if (input_file) {
			if (language_for_file(argv[i])) {
				cc_log("Multiple input files: %s and %s", input_file, argv[i]);
				stats_update(STATS_MULTIPLE);
			} else if (!found_c_opt) {
				cc_log("Called for link with %s", argv[i]);
				if (strstr(argv[i], "conftest.")) {
					stats_update(STATS_CONFTEST);
				} else {
					stats_update(STATS_LINK);
				}
			} else {
				cc_log("Unsupported source extension: %s", argv[i]);
				stats_update(STATS_SOURCELANG);
			}
			result = false;
			goto out;
		}

		/* Rewrite to relative to increase hit rate. */
		input_file = make_relative_path(x_strdup(argv[i]));
	}

	if (!input_file) {
		cc_log("No input file found");
		stats_update(STATS_NOINPUT);
		result = false;
		goto out;
	}

	if (found_pch || found_fpch_preprocess) {
		using_precompiled_header = true;
		if (!(sloppiness & SLOPPY_TIME_MACROS)) {
			cc_log("You have to specify \"time_macros\" sloppiness when using"
			       " precompiled headers to get direct hits");
			cc_log("Disabling direct mode");
			stats_update(STATS_CANTUSEPCH);
			result = false;
			goto out;
		}
	}

	if(force_preprocessor_type == 0) { 
		actual_language = language_for_file(input_file);
	} 
	else if(force_preprocessor_type == 2) { 
		actual_language = "c++";
	}
	else {
		actual_language = "c";
	}

	output_is_precompiled_header =
		actual_language && strstr(actual_language, "-header") != NULL;

	if (!found_c_opt && !output_is_precompiled_header) {
		cc_log("No -c option found");
		/* I find that having a separate statistic for autoconf tests is useful,
		   as they are the dominant form of "called for link" in many cases */
		if (strstr(input_file, "conftest.")) {
			stats_update(STATS_CONFTEST);
		} else {
			stats_update(STATS_LINK);
		}
		result = false;
		goto out;
	}

	if (!actual_language) {
		cc_log("Unsupported source extension: %s", input_file);
		stats_update(STATS_SOURCELANG);
		result = false;
		goto out;
	}

	direct_i_file = language_is_preprocessed(actual_language);

	if (output_is_precompiled_header) {
		/* It doesn't work to create the .gch from preprocessed source. */
		cc_log("Creating precompiled header; not compiling preprocessed code");
		compile_preprocessed_source_code = false;
	}

	/* don't try to second guess the compilers heuristics for stdout handling */
	if (output_obj && str_eq(output_obj, "-")) {
		stats_update(STATS_OUTSTDOUT);
		cc_log("Output file is -");
		result = false;
		goto out;
	}

	if (!output_obj) {
		if (output_is_precompiled_header) {
			output_obj = format("%s.gch", input_file);
		} else {
			char *p;
			output_obj = x_strdup(input_file);
			if ((p = strrchr(output_obj, '/'))) {
				output_obj = p+1;
			}
			p = strrchr(output_obj, '.');
			if (!p || !p[1]) {
				cc_log("Badly formed object filename");
				stats_update(STATS_ARGS);
				result = false;
				goto out;
			}
			p[1] = found_S_opt ? 's' : 'o';
			p[2] = 0;
		}
	}

	/* cope with -o /dev/null */
	if (!str_eq(output_obj,"/dev/null")
	    && stat(output_obj, &st) == 0
	    && !S_ISREG(st.st_mode)) {
		cc_log("Not a regular file: %s", output_obj);
		stats_update(STATS_DEVICE);
		result = false;
		goto out;
	}

	/*
	 * Some options shouldn't be passed to the real compiler when it compiles
	 * preprocessed code:
	 *
	 * -finput-charset=XXX (otherwise conversion happens twice)
	 * -x XXX (otherwise the wrong language is selected)
	 */
	*preprocessor_args = args_copy(stripped_args);
	if (input_charset) {
		args_add(*preprocessor_args, input_charset);
	}
	if (found_pch) {
		args_add(*preprocessor_args, "-fpch-preprocess");
	}

	/*
	 * Add flags for dependency generation only to the preprocessor command line.
	 */
	if (generating_dependencies) {
		char *dep_path;

		if(!dependency_filename_specified)
		{ 
			char *base_name;

			base_name = remove_extension(output_obj);
			dep_file = format("%s.d", base_name);
			free(base_name);
		}

		if (!dependency_target_specified) {
			args_add(dep_args, "--depend_target");
			args_add(dep_args, output_obj);
		}

		free(output_dep);
		
		if(dep_dir)
		{ 
#ifdef _WIN32
			dep_path = make_relative_path(format("%s\\%s", dep_dir, dep_file));
#else
			dep_path = make_relative_path(format("%s/%s", dep_dir, dep_file));
#endif
		}
		else
		{ 
			dep_path = x_strdup(dep_file);
		}

		args_add(dep_args, "--depend");
		args_add(dep_args, dep_path);
		/* dep_path will be free in make_relative_path */
		output_dep = make_relative_path(x_strdup(dep_path));
	}

	if (compile_preprocessed_source_code) {
		*compiler_args = args_copy(stripped_args);
	} else {
		*compiler_args = args_copy(*preprocessor_args);
	}

	i_extension = getenv("CCACHE_EXTENSION");
	if (!i_extension) {
		const char *p_language = p_language_for_language(actual_language);
		i_extension = extension_for_language(p_language) + 1;

	}
	/* Patch for preprocessed file extension for armcc 3.1.
	 * armcc 3.1 cannot recognize "i" or "ii" as the preprocessed source file 
	 * without --compile_all_input. */
	args_add(*compiler_args, "--compile_all_input");
	if (str_eq(i_extension, "ii")) { 
		args_add(*compiler_args, "--cpp");
	}

	/*
	 * Only pass dependency arguments to the preprocesor since Intel's C++
	 * compiler doesn't produce a correct .d file when compiling preprocessed
	 * source.
	 */
	args_extend(*preprocessor_args, dep_args);

out:
	free(dep_file);
	free(dep_dir);
	args_free(stripped_args);
	args_free(dep_args);
	return result;
}
Ejemplo n.º 8
0
/*
 * Process the compiler options into options suitable for passing to the
 * preprocessor and the real compiler. The preprocessor options don't include
 * -E; this is added later. Returns true on success, otherwise false.
 */
bool
c166_process_args(struct args *orig_args, struct args **preprocessor_args,
                struct args **compiler_args)
{
	int i;
	bool found_c_opt = false;
	bool found_S_opt = false;
	bool found_H_opt = false;
	/* 0: Choose preprocessor type by the file extension.
	 * 1: Use c preprocessor.
	 * 2: Use c++ preprocessor.*/
	unsigned force_preprocessor_type = 0;
	const char *actual_language;          /* Language to actually use. */
	struct stat st;
	/* is the dependency makefile name overridden with -MF? */
	bool dependency_filename_specified = false;
	/* is the dependency makefile target name specified with -MT or -MQ? */
	bool dependency_target_specified = false;
	struct args *stripped_args = NULL, *dep_args = NULL, *h_args;
	int argc = orig_args->argc;
	char **argv = orig_args->argv;
	bool result = true;

	stripped_args = args_init(0, NULL);
	dep_args = args_init(0, NULL);
	h_args = args_init(0, NULL);

	args_add(stripped_args, argv[0]);

	for (i = 1; i < argc; i++) {
		/* The user knows best: just swallow the next arg */
		if (str_eq(argv[i], "--ccache-skip")) {
			i++;
			if (i == argc) {
				cc_log("--ccache-skip lacks an argument");
				result = false;
				goto out;
			}
			args_add(stripped_args, argv[i]);
			continue;
		}

		/* Special case for -E. */
		if (str_eq(argv[i], "-E")) {
			stats_update(STATS_PREPROCESSING);
			result = false;
			goto out;
		}

		/* These are always too hard. */
		if (compopt_too_hard(argv[i])) {
			cc_log("Compiler option %s is unsupported", argv[i]);
			stats_update(STATS_UNSUPPORTED);
			result = false;
			goto out;
		}

		/* These are too hard in direct mode. */
		if (enable_direct) {
			if (compopt_too_hard_for_direct_mode(argv[i])) {
				cc_log("Unsupported compiler option for direct mode: %s", argv[i]);
				enable_direct = false;
			}
		}

		/* we must have -c */
		if (str_eq(argv[i], "-c")) {
			args_add(stripped_args, argv[i]);
			found_c_opt = true;
			continue;
		}

		/* -S changes the default extension */
		/* TODO: Check this -S out!
		if (str_eq(argv[i], "-S")) {
			args_add(stripped_args, argv[i]);
			found_S_opt = true;
			continue;
		}
		*/

		/* we need to work out where the output was meant to go */
		if (str_eq(argv[i], "-o")) {
			if (i == argc-1) {
				cc_log("Missing argument to %s", argv[i]);
				stats_update(STATS_ARGS);
				result = false;
				goto out;
			}
			output_obj = argv[i+1];
			i++;
			continue;
		}

		/* alternate form of -o, with no space */
		if (str_startswith(argv[i], "-o")) {
			output_obj = &argv[i][2];
			continue;
		}

		/* debugging is handled specially, so that we know if we
		   can strip line number info
		*/
		if (str_startswith(argv[i], "-g")) {
			args_add(stripped_args, argv[i]);
			if (enable_unify) {
				cc_log("%s used; disabling unify mode", argv[i]);
				enable_unify = false;
			}
			continue;
		}

		if (str_startswith(argv[i], "-H")) {
			cc_log("Detected -H %s", argv[i]);
			args_add(h_args, argv[i]);
			found_H_opt = true;
			continue;
		}

		/*
		 * Options taking an argument that that we may want to rewrite
		 * to relative paths to get better hit rate. A secondary effect
		 * is that paths in the standard error output produced by the
		 * compiler will be normalized.
		 */
		if (compopt_takes_path(argv[i])) {
			char *relpath;
			if (i == argc-1) {
				cc_log("Missing argument to %s", argv[i]);
				stats_update(STATS_ARGS);
				result = false;
				goto out;
			}

			args_add(stripped_args, argv[i]);
			relpath = make_relative_path(x_strdup(argv[i+1]));
			args_add(stripped_args, relpath);

			free(relpath);
			i++;
			continue;
		}

		/* Same as above but options with concatenated argument. */
		if (compopt_short(compopt_takes_path, argv[i])) {
			char *relpath;
			char *option;
			relpath = make_relative_path(x_strdup(argv[i] + 2));
			option = format("-%c%s", argv[i][1], relpath);
			args_add(stripped_args, option);
			free(relpath);
			free(option);
			continue;
		}

		/* options that take an argument */
		if (compopt_takes_arg(argv[i])) {
			if (i == argc-1) {
				cc_log("Missing argument to %s", argv[i]);
				stats_update(STATS_ARGS);
				result = false;
				goto out;
			}
			args_add(stripped_args, argv[i]);
			args_add(stripped_args, argv[i+1]);
			i++;
			continue;
		}

		if (str_eq(argv[i], "-c++")) {
			force_preprocessor_type = 2;
			args_add(stripped_args, argv[i]);
			continue;
		}
		if (str_eq(argv[i], "-noc++")) {
			force_preprocessor_type = 1;
			args_add(stripped_args, argv[i]);
			continue;
		}

		/* other options */
		if (argv[i][0] == '-') {
			args_add(stripped_args, argv[i]);
			continue;
		}

		/* if an argument isn't a plain file then assume its
		   an option, not an input file. This allows us to
		   cope better with unusual compiler options */
		if (stat(argv[i], &st) != 0 || !S_ISREG(st.st_mode)) {
			cc_log("%s is not a regular file, not considering as input file",
			       argv[i]);
			args_add(stripped_args, argv[i]);
			continue;
		}

		if (input_file) {
			if (language_for_file(argv[i])) {
				cc_log("Multiple input files: %s and %s", input_file, argv[i]);
				stats_update(STATS_MULTIPLE);
			} else if (!found_c_opt) {
				cc_log("Called for link with %s", argv[i]);
				if (strstr(argv[i], "conftest.")) {
					stats_update(STATS_CONFTEST);
				} else {
					stats_update(STATS_LINK);
				}
			} else {
				cc_log("Unsupported source extension: %s", argv[i]);
				stats_update(STATS_SOURCELANG);
			}
			result = false;
			goto out;
		}

		/* Rewrite to relative to increase hit rate. */
		input_file = make_relative_path(x_strdup(argv[i]));
	}

	if (!input_file) {
		cc_log("No input file found");
		stats_update(STATS_NOINPUT);
		result = false;
		goto out;
	}

	if(force_preprocessor_type == 0) { 
		actual_language = language_for_file(input_file);
	} 
	else if(force_preprocessor_type == 2) { 
		actual_language = "c++";
	}
	else {
		actual_language = "c";
	}
	
	output_is_precompiled_header =
		actual_language && strstr(actual_language, "-header") != NULL;

	if (!found_c_opt && !output_is_precompiled_header) {
		cc_log("No -c option found");
		/* I find that having a separate statistic for autoconf tests is useful,
		   as they are the dominant form of "called for link" in many cases */
		if (strstr(input_file, "conftest.")) {
			stats_update(STATS_CONFTEST);
		} else {
			stats_update(STATS_LINK);
		}
		result = false;
		goto out;
	}

	if (!actual_language) {
		cc_log("Unsupported source extension: %s", input_file);
		stats_update(STATS_SOURCELANG);
		result = false;
		goto out;
	}

	direct_i_file = language_is_preprocessed(actual_language);

	if (output_is_precompiled_header) {
		/* It doesn't work to create the .gch from preprocessed source. */
		cc_log("Creating precompiled header; not compiling preprocessed code");
		compile_preprocessed_source_code = false;
	}

	i_extension = getenv("CCACHE_EXTENSION");
	if (!i_extension) {
		const char *p_language = p_language_for_language(actual_language);
		if(str_eq(p_language, "c++-cpp-output")) { 
			/* Dirty fix for preprocessed file extension for cc166.
			 * The cp166 cannot handle cpp files with extension ii.*/
			i_extension = "ii.cpp";
		}
		else { 
			i_extension = extension_for_language(p_language) + 1;
		}
	}

	/* don't try to second guess the compilers heuristics for stdout handling */
	if (output_obj && str_eq(output_obj, "-")) {
		stats_update(STATS_OUTSTDOUT);
		cc_log("Output file is -");
		result = false;
		goto out;
	}

	if (!output_obj) {
		if (output_is_precompiled_header) {
			output_obj = format("%s.gch", input_file);
		} else {
			char *p;
			output_obj = x_strdup(input_file);
			if ((p = strrchr(output_obj, '/'))) {
				output_obj = p+1;
			}
			p = strrchr(output_obj, '.');
			if (!p || !p[1]) {
				cc_log("Badly formed object filename");
				stats_update(STATS_ARGS);
				result = false;
				goto out;
			}
			*p = 0;
			p = output_obj;
			if(found_S_opt)
			{ 
				output_obj = format("%s.s", p);
			}
			else
			{ /*The default extension of object file is obj for c166.*/
				output_obj = format("%s.obj", p);
			}
			free(p);
		}
	}

	/* cope with -o /dev/null */
	if (!str_eq(output_obj,"/dev/null")
	    && stat(output_obj, &st) == 0
	    && !S_ISREG(st.st_mode)) {
		cc_log("Not a regular file: %s", output_obj);
		stats_update(STATS_DEVICE);
		result = false;
		goto out;
	}

	/*
	 * Some options shouldn't be passed to the real compiler when it compiles
	 * preprocessed code:
	 */
	*preprocessor_args = args_copy(stripped_args);
	/* Args with -H has been already preprocessed.
	 * If it passed to the compiler again, some type redefined error will pop up.*/
	if (found_H_opt) {
		args_extend(*preprocessor_args, h_args);
	}

	/*
	 * Add flags for dependency generation only to the preprocessor command line.
	 */
	if (generating_dependencies) {
		if (!dependency_filename_specified) {
			char *default_depfile_name;
			char *base_name;

			base_name = remove_extension(output_obj);
			default_depfile_name = format("%s.d", base_name);
			free(base_name);
			args_add(dep_args, "-MF");
			args_add(dep_args, default_depfile_name);
			output_dep = make_relative_path(x_strdup(default_depfile_name));
		}

		if (!dependency_target_specified) {
			args_add(dep_args, "-MQ");
			args_add(dep_args, output_obj);
		}
	}

	if (compile_preprocessed_source_code) {
		*compiler_args = args_copy(stripped_args);
	} else {
		*compiler_args = args_copy(*preprocessor_args);
	}
	
	/* Due to bugs or cc166 v8.6r3, the behaviours of c/c++ preprocessor 
	 * are quite different.
	 * When using cpp preprocessor, the output will be directly send to stdout like gcc.
	 * When using c preprocessor, the output will be written to filename.i even without "-o".*/
	if(str_eq(actual_language, "c"))
	{ 
#ifdef _WIN32
#error Never test this in Windows.
#else
		args_add(*preprocessor_args, "-o/dev/stdout");
#endif
	}

	/*
	 * Only pass dependency arguments to the preprocesor since Intel's C++
	 * compiler doesn't produce a correct .d file when compiling preprocessed
	 * source.
	 */
	args_extend(*preprocessor_args, dep_args);

out:
	args_free(stripped_args);
	args_free(dep_args);
	args_free(h_args);

	return result;
}
Ejemplo n.º 9
0
Archivo: args.c Proyecto: ccache/ccache
struct args *
args_init_from_gcc_atfile(const char *filename)
{
	char *argtext;
	if (!(argtext = read_text_file(filename, 0))) {
		return NULL;
	}

	struct args *args = args_init(0, NULL);
	char *pos = argtext;
	char *argbuf = x_malloc(strlen(argtext) + 1);
	char *argpos = argbuf;

	// Used to track quoting state; if \0, we are not inside quotes. Otherwise
	// stores the quoting character that started it, for matching the end quote.
	char quoting = '\0';

	while (1) {
		switch (*pos) {
		case '\\':
			pos++;
			if (*pos == '\0') {
				continue;
			}
			break;

		case '\"':
		case '\'':
			if (quoting != '\0') {
				if (quoting == *pos) {
					quoting = '\0';
					pos++;
					continue;
				} else {
					break;
				}
			} else {
				quoting = *pos;
				pos++;
				continue;
			}

		case '\n':
		case '\r':
		case '\t':
		case ' ':
			if (quoting) {
				break;
			}
		// Fall through.

		case '\0':
			// End of token
			*argpos = '\0';
			if (argbuf[0] != '\0') {
				args_add(args, argbuf);
			}
			argpos = argbuf;
			if (*pos == '\0') {
				goto out;
			} else {
				pos++;
				continue;
			}
		}

		*argpos = *pos;
		pos++;
		argpos++;
	}

out:
	free(argbuf);
	free(argtext);
	return args;
}
Ejemplo n.º 10
0
/* Fork a new child with a pty. */
gboolean pty_child_fork(struct child* c, gint new_stdin_fd,
		gint new_stdout_fd, gint new_stderr_fd) {

	gint pty_slave_fd = -1;
	gint pty_master_fd = -1;
	const gchar* pty_slave_name = NULL;

	c->args.argv[0] = c->exec_name;

	/* Delimit the args with NULL. */
	args_add(&(c->args), NULL);

	/* Setup a pty for the new shell. */
	/* Get master (pty) */
	if ((pty_master_fd = rxvt_get_pty(&pty_slave_fd, &pty_slave_name)) < 0) {
		g_critical("Could not open master side of pty");
		c->pid = -1;
		c->fd_in = -1;
		c->fd_out = -1;
		return FALSE;
	}

	/* Turn on non-blocking -- probably not necessary since we're in raw
	   terminal mode. */
	fcntl(pty_master_fd, F_SETFL, O_NDELAY);

	/* This will be the interface with the new shell. */
	c->fd_in = pty_master_fd;
	c->fd_out = pty_master_fd;

	switch (c->pid = fork()) {
		case -1:
			g_critical("Could not fork process: %s", g_strerror(errno));
			return FALSE;
			/*break;*/

		case 0:
			if (setsid() == -1) {
				g_critical("pty_child_fork(): setsid(): %s",
						g_strerror(errno));
				goto child_fail;
			}

			/* Get slave (tty) */
			if (pty_slave_fd < 0) {
				if ((pty_slave_fd = rxvt_get_tty(pty_slave_name)) < 0) {
					(void) close(pty_master_fd);
					g_critical("Could not open slave tty \"%s\"",
							pty_slave_name);
					goto child_fail;
				}
			}
			if (rxvt_control_tty(pty_slave_fd, pty_slave_name) < 0) {
				g_critical("Could not obtain control of tty \"%s\"",
						pty_slave_name);
				goto child_fail;
			}

			/* A parameter of NEW_PTY_FD means to use the slave side of the
			   new pty.  A parameter of CLOSE_FD means to just close that fd
			   right out. */
			if (new_stdin_fd == NEW_PTY_FD)
				new_stdin_fd = pty_slave_fd;
			if (new_stdout_fd == NEW_PTY_FD)
				new_stdout_fd = pty_slave_fd;
			if (new_stderr_fd == NEW_PTY_FD)
				new_stderr_fd = pty_slave_fd;

			if (new_stdin_fd == CLOSE_FD)
				(void)close(STDIN_FILENO);
			else if (dup2(new_stdin_fd, STDIN_FILENO) == -1) {
				g_critical("Could not replace stdin in child process: %s",
						g_strerror(errno));
				goto child_fail;
			}

			if (new_stdout_fd == CLOSE_FD)
				(void)close(STDOUT_FILENO);
			else if (dup2(new_stdout_fd, STDOUT_FILENO) == -1) {
				g_critical("Could not replace stdout in child process: %s",
						g_strerror(errno));
				goto child_fail;
			}

			if (new_stderr_fd == CLOSE_FD)
				(void)close(STDERR_FILENO);
			else if (dup2(new_stderr_fd, STDERR_FILENO) == -1) {
				g_critical("Could not replace stderr in child process: %s",
						g_strerror(errno));
				goto child_fail;
			}

			(void)close(pty_slave_fd);
			execvp(c->exec_name, c->args.argv);

			child_fail:
			g_critical("Exec failed: %s", g_strerror(errno));
			g_critical("If vgseer does not exit, you should do so manually");
			_exit(EXIT_FAILURE);
			break;
	}

	if (!wait_for_data(c->fd_out)) {
		g_critical("Did not receive go-ahead from child shell");
		return FALSE;
	}

	return TRUE;
}
Ejemplo n.º 11
0
Archivo: args.c Proyecto: mtexier/ucc
void args_process(int argc, char **argv)
{
	int i;

	for(i = 1; i < argc; i++){
		if(!strcmp(argv[i], "-d")){
			verbose = 1;
		}else if(!strcmp(argv[i], "-g")){
			debug = 1;
		}else if(!strcmp(argv[i], "-w")){
			no_warn = 1;
		}else if(!strncmp(argv[i], "-nost", 5)){
			if(!strcmp(argv[i] + 5, "artfiles"))
				no_startfiles = 1;
			else if(!strcmp(argv[i] + 5, "dlib"))
				no_stdlib = 1;
			else
				goto usage;
		}else if(!strcmp(argv[i], "--help")){
usage:
			fprintf(stderr,
					"Usage: %s [-Wwarning...] [-foption...] [-[ESc]] [-o output] input\n"
					"Other options:\n"
					"  -nost{dlib,artfiles} - don't like with stdlib/crt.o\n"
					"  -no-rm - don't remove temporary files\n"
					"  -d - run in debug/verbose mode\n"
					"  -X backend - specify cc1 backend\n"
					"  -x frontend - specify starting point (c, cpp-output and asm)\n",
					*argv);
			return 1;
		}else if(!strcmp(argv[i], "-no-rm")){
			do_rm = 0;
		}else{
			if(argv[i][0] == '-'){
				unsigned int j;
				int found;

				switch(argv[i][1]){
					case 'f':
					case 'W':
						args_add(MODE_COMPILE, argv[i]);
						continue;
					case 'D':
					case 'U':
					case 'I':
						args_add(MODE_PREPROCESS, argv[i]);
						continue;
					case 'l':
					case 'L':
						args_add(MODE_LINK, argv[i]);
						continue;
				}

				if(!argv[i][2]){
					found = 0;
					for(j = 0; j < 3; j++)
						if(argv[i][1] == modes[j].arg){
							out_mode = j;
							found = 1;
							break;
						}

					if(found)
						continue;
				}

				found = 0;
				for(j = 0; j < sizeof(opts) / sizeof(opts[0]); j++)
					if(argv[i][1] == opts[j].optn){
						if(argv[i][2]){
							*opts[j].ptr = argv[i] + 2;
						}else{
							if(!argv[++i])
								goto usage;
							*opts[j].ptr = argv[i];
						}
						found = 1;
						break;
					}

				if(found)
					continue;
			}

			inputs = argv + i;
			break;
		}
	}
}
Ejemplo n.º 12
0
/* 
   process the compiler options to form the correct set of options 
   for obtaining the preprocessor output
*/
static void process_args(int argc, char **argv)
{
	int i;
	int found_c_opt = 0;
	int found_S_opt = 0;
	struct stat st;
	char *e;

	stripped_args = args_init(0, NULL);

	args_add(stripped_args, argv[0]);

	for (i=1; i<argc; i++) {
		/* some options will never work ... */
		if (strcmp(argv[i], "-E") == 0) {
			failed();
		}

		/* these are too hard */
		if (strcmp(argv[i], "-fbranch-probabilities")==0 ||
		    strcmp(argv[i], "-M") == 0 ||
		    strcmp(argv[i], "-MM") == 0 ||
		    strcmp(argv[i], "-x") == 0) {
			cc_log("argument %s is unsupported\n", argv[i]);
			stats_update(STATS_UNSUPPORTED);
			failed();
			continue;
		}

		/* we must have -c */
		if (strcmp(argv[i], "-c") == 0) {
			args_add(stripped_args, argv[i]);
			found_c_opt = 1;
			continue;
		}

		/* -S changes the default extension */
		if (strcmp(argv[i], "-S") == 0) {
			args_add(stripped_args, argv[i]);
			found_S_opt = 1;
			continue;
		}
		
		/* we need to work out where the output was meant to go */
		if (strcmp(argv[i], "-o") == 0) {
			if (i == argc-1) {
				cc_log("missing argument to %s\n", argv[i]);
				stats_update(STATS_ARGS);
				failed();
			}
			output_file = argv[i+1];
			i++;
			continue;
		}
		
		/* alternate form of -o, with no space */
		if (strncmp(argv[i], "-o", 2) == 0) {
			output_file = &argv[i][2];
			continue;
		}

		/* debugging is handled specially, so that we know if we
		   can strip line number info 
		*/
		if (strncmp(argv[i], "-g", 2) == 0) {
			args_add(stripped_args, argv[i]);
			if (strcmp(argv[i], "-g0") != 0) {
				enable_unify = 0;
			}
			continue;
		}

		/* The user knows best: just swallow the next arg */
		if (strcmp(argv[i], "--ccache-skip") == 0) {
			i++;
			if (i == argc) {
				failed();
			}
			args_add(stripped_args, argv[i]);
			continue;
		}

		/* options that take an argument */
		{
			const char *opts[] = {"-I", "-include", "-imacros", "-iprefix",
					      "-iwithprefix", "-iwithprefixbefore",
					      "-L", "-D", "-U", "-x", "-MF", 
					      "-MT", "-MQ", "-isystem", "-aux-info",
					      "--param", "-A", "-Xlinker", "-u",
					      "-idirafter", 
					      NULL};
			int j;
			for (j=0;opts[j];j++) {
				if (strcmp(argv[i], opts[j]) == 0) {
					if (i == argc-1) {
						cc_log("missing argument to %s\n", 
						       argv[i]);
						stats_update(STATS_ARGS);
						failed();
					}
						
					args_add(stripped_args, argv[i]);
					args_add(stripped_args, argv[i+1]);
					i++;
					break;
				}
			}
			if (opts[j]) continue;
		}

		/* other options */
		if (argv[i][0] == '-') {
			args_add(stripped_args, argv[i]);
			continue;
		}

		/* if an argument isn't a plain file then assume its
		   an option, not an input file. This allows us to
		   cope better with unusual compiler options */
		if (stat(argv[i], &st) != 0 || !S_ISREG(st.st_mode)) {
			args_add(stripped_args, argv[i]);
			continue;			
		}

		if (input_file) {
			if (check_extension(argv[i], NULL)) {
				cc_log("multiple input files (%s and %s)\n",
				       input_file, argv[i]);
				stats_update(STATS_MULTIPLE);
			} else if (!found_c_opt) {
				cc_log("called for link with %s\n", argv[i]);
				if (strstr(argv[i], "conftest.")) {
					stats_update(STATS_CONFTEST);
				} else {
					stats_update(STATS_LINK);
				}
			} else {
				cc_log("non C/C++ file %s\n", argv[i]);
				stats_update(STATS_NOTC);
			}
			failed();
		}

		input_file = argv[i];
	}

	if (!input_file) {
		cc_log("No input file found\n");
		stats_update(STATS_NOINPUT);
		failed();
	}

	i_extension = check_extension(input_file, &direct_i_file);
	if (i_extension == NULL) {
		cc_log("Not a C/C++ file - %s\n", input_file);
		stats_update(STATS_NOTC);
		failed();
	}

	if (!found_c_opt) {
		cc_log("No -c option found for %s\n", input_file);
		/* I find that having a separate statistic for autoconf tests is useful,
		   as they are the dominant form of "called for link" in many cases */
		if (strstr(input_file, "conftest.")) {
			stats_update(STATS_CONFTEST);
		} else {
			stats_update(STATS_LINK);
		}
		failed();
	}


	/* don't try to second guess the compilers heuristics for stdout handling */
	if (output_file && strcmp(output_file, "-") == 0) {
		stats_update(STATS_OUTSTDOUT);
		failed();
	}

	if (!output_file) {
		char *p;
		output_file = x_strdup(input_file);
		if ((p = strrchr(output_file, '/'))) {
			output_file = p+1;
		}
		p = strrchr(output_file, '.');
		if (!p || !p[1]) {
			cc_log("badly formed output_file %s\n", output_file);
			stats_update(STATS_ARGS);
			failed();
		}
		p[1] = found_S_opt ? 's' : 'o';
		p[2] = 0;
	}

	/* cope with -o /dev/null */
	if (strcmp(output_file,"/dev/null") != 0 && stat(output_file, &st) == 0 && !S_ISREG(st.st_mode)) {
		cc_log("Not a regular file %s\n", output_file);
		stats_update(STATS_DEVICE);
		failed();
	}

	if ((e=getenv("CCACHE_PREFIX"))) {
		char *p = find_executable(e, MYNAME);
		if (!p) {
			perror(e);
			exit(1);
		}
		args_add_prefix(stripped_args, p);
	}
}
Ejemplo n.º 13
0
/* find the hash for a command. The hash includes all argument lists,
   plus the output from running the compiler with -E */
static void find_hash(ARGS *args)
{
	int i;
	char *path_stdout, *path_stderr;
	char *hash_dir;
	char *s;
	struct stat st;
	int status;
	int nlevels = 2;
	char *input_base;
	char *tmp;
	
	if ((s = getenv("CCACHE_NLEVELS"))) {
		nlevels = atoi(s);
		if (nlevels < 1) nlevels = 1;
		if (nlevels > 8) nlevels = 8;
	}

	hash_start();

	/* when we are doing the unifying tricks we need to include
           the input file name in the hash to get the warnings right */
	if (enable_unify) {
		hash_string(input_file);
	}

	/* we have to hash the extension, as a .i file isn't treated the same
	   by the compiler as a .ii file */
	hash_string(i_extension);

	/* first the arguments */
	for (i=1;i<args->argc;i++) {
		/* some arguments don't contribute to the hash. The
		   theory is that these arguments will change the
		   output of -E if they are going to have any effect
		   at all, or they only affect linking */
		if (i < args->argc-1) {
			if (strcmp(args->argv[i], "-I") == 0 ||
			    strcmp(args->argv[i], "-include") == 0 ||
			    strcmp(args->argv[i], "-L") == 0 ||
			    strcmp(args->argv[i], "-D") == 0 ||
			    strcmp(args->argv[i], "-idirafter") == 0 ||
			    strcmp(args->argv[i], "-isystem") == 0) {
				i++;
				continue;
			}
		}
		if (strncmp(args->argv[i], "-I", 2) == 0 ||
		    strncmp(args->argv[i], "-L", 2) == 0 ||
		    strncmp(args->argv[i], "-D", 2) == 0 ||
		    strncmp(args->argv[i], "-idirafter", 10) == 0 ||
		    strncmp(args->argv[i], "-isystem", 8) == 0) {
			continue;
		}

		if (strncmp(args->argv[i], "--specs=", 8) == 0 &&
		    stat(args->argv[i]+8, &st) == 0) {
			/* if given a explicit specs file, then hash that file, but
			   don't include the path to it in the hash */
			hash_file(args->argv[i]+8);
			continue;
		}

		/* all other arguments are included in the hash */
		hash_string(args->argv[i]);
	}

	/* the compiler driver size and date. This is a simple minded way
	   to try and detect compiler upgrades. It is not 100% reliable */
	if (stat(args->argv[0], &st) != 0) {
		cc_log("Couldn't stat the compiler!? (argv[0]='%s')\n", args->argv[0]);
		stats_update(STATS_COMPILER);
		failed();
	}

	/* also include the hash of the compiler name - as some compilers
	   use hard links and behave differently depending on the real name */
	if (st.st_nlink > 1) {
		hash_string(str_basename(args->argv[0]));
	}

	hash_int(st.st_size);
	hash_int(st.st_mtime);

	/* possibly hash the current working directory */
	if (getenv("CCACHE_HASHDIR")) {
		char *cwd = gnu_getcwd();
		if (cwd) {
			hash_string(cwd);
			free(cwd);
		}
	}

	/* ~/hello.c -> tmp.hello.123.i 
	   limit the basename to 10
	   characters in order to cope with filesystem with small
	   maximum filename length limits */
	input_base = str_basename(input_file);
	tmp = strchr(input_base, '.');
	if (tmp != NULL) {
		*tmp = 0;
	}
	if (strlen(input_base) > 10) {
		input_base[10] = 0;
	}

	/* now the run */
	x_asprintf(&path_stdout, "%s/%s.tmp.%s.%s", temp_dir,
		   input_base, tmp_string(), 
		   i_extension);
	x_asprintf(&path_stderr, "%s/tmp.cpp_stderr.%s", temp_dir, tmp_string());

	if (!direct_i_file) {
		/* run cpp on the input file to obtain the .i */
		args_add(args, "-E");
		args_add(args, input_file);
		status = execute(args->argv, path_stdout, path_stderr);
		args_pop(args, 2);
	} else {
		/* we are compiling a .i or .ii file - that means we
		   can skip the cpp stage and directly form the
		   correct i_tmpfile */
		path_stdout = input_file;
		if (create_empty_file(path_stderr) != 0) {
			stats_update(STATS_ERROR);
			cc_log("failed to create empty stderr file\n");
			failed();
		}
		status = 0;
	}

	if (status != 0) {
		if (!direct_i_file) {
			unlink(path_stdout);
		}
		unlink(path_stderr);
		cc_log("the preprocessor gave %d\n", status);
		stats_update(STATS_PREPROCESSOR);
		failed();
	}

	/* if the compilation is with -g then we have to include the whole of the
	   preprocessor output, which means we are sensitive to line number
	   information. Otherwise we can discard line number info, which makes
	   us less sensitive to reformatting changes 

	   Note! I have now disabled the unification code by default
	   as it gives the wrong line numbers for warnings. Pity.
	*/
	if (!enable_unify) {
		hash_file(path_stdout);
	} else {
		if (unify_hash(path_stdout) != 0) {
			stats_update(STATS_ERROR);
			failed();
		}
	}
	hash_file(path_stderr);

	i_tmpfile = path_stdout;

	if (!getenv("CCACHE_CPP2")) {
		/* if we are using the CPP trick then we need to remember this stderr
		   data and output it just before the main stderr from the compiler
		   pass */
		cpp_stderr = path_stderr;
	} else {	
		unlink(path_stderr);
		free(path_stderr);
	}

	/* we use a N level subdir for the cache path to reduce the impact
	   on filesystems which are slow for large directories
	*/
	s = hash_result();
	x_asprintf(&hash_dir, "%s/%c", cache_dir, s[0]);
	x_asprintf(&stats_file, "%s/stats", hash_dir);
	for (i=1; i<nlevels; i++) {
		char *p;
		if (create_dir(hash_dir) != 0) {
			cc_log("failed to create %s\n", hash_dir);
			failed();
		}
		x_asprintf(&p, "%s/%c", hash_dir, s[i]);
		free(hash_dir);
		hash_dir = p;
	}
	if (create_dir(hash_dir) != 0) {
		cc_log("failed to create %s\n", hash_dir);
		failed();
	}
	x_asprintf(&hashname, "%s/%s", hash_dir, s+nlevels);
	free(hash_dir);
}
Ejemplo n.º 14
0
/* run the real compiler and put the result in cache */
static void to_cache(ARGS *args)
{
	char *path_stderr;
	char *tmp_stdout, *tmp_stderr, *tmp_hashname;
	struct stat st1, st2;
	int status;

	x_asprintf(&tmp_stdout, "%s/tmp.stdout.%s", temp_dir, tmp_string());
	x_asprintf(&tmp_stderr, "%s/tmp.stderr.%s", temp_dir, tmp_string());
	x_asprintf(&tmp_hashname, "%s/tmp.hash.%s.o", temp_dir, tmp_string());

	args_add(args, "-o");
	args_add(args, tmp_hashname);

	/* Turn off DEPENDENCIES_OUTPUT when running cc1, because
	 * otherwise it will emit a line like
	 *
	 *  tmp.stdout.vexed.732.o: /home/mbp/.ccache/tmp.stdout.vexed.732.i
	 *
	 * unsetenv() is on BSD and Linux but not portable. */
	putenv("DEPENDENCIES_OUTPUT");
	
	if (getenv("CCACHE_CPP2")) {
		args_add(args, input_file);
	} else {
		args_add(args, i_tmpfile);
	}
	status = execute(args->argv, tmp_stdout, tmp_stderr);
	args_pop(args, 3);

	if (stat(tmp_stdout, &st1) != 0 || st1.st_size != 0) {
		cc_log("compiler produced stdout for %s\n", output_file);
		stats_update(STATS_STDOUT);
		unlink(tmp_stdout);
		unlink(tmp_stderr);
		unlink(tmp_hashname);
		failed();
	}
	unlink(tmp_stdout);

	if (status != 0) {
		int fd;
		cc_log("compile of %s gave status = %d\n", output_file, status);
		stats_update(STATS_STATUS);

		fd = open(tmp_stderr, O_RDONLY | O_BINARY);
		if (fd != -1) {
			if (strcmp(output_file, "/dev/null") == 0 ||
			    rename(tmp_hashname, output_file) == 0 || errno == ENOENT) {
				if (cpp_stderr) {
					/* we might have some stderr from cpp */
					int fd2 = open(cpp_stderr, O_RDONLY | O_BINARY);
					if (fd2 != -1) {
						copy_fd(fd2, 2);
						close(fd2);
						unlink(cpp_stderr);
						cpp_stderr = NULL;
					}
				}

				/* we can use a quick method of
                                   getting the failed output */
				copy_fd(fd, 2);
				close(fd);
				unlink(tmp_stderr);
				if (i_tmpfile && !direct_i_file) {
					unlink(i_tmpfile);
				}
				exit(status);
			}
		}
		
		unlink(tmp_stderr);
		unlink(tmp_hashname);
		failed();
	}

	x_asprintf(&path_stderr, "%s.stderr", hashname);

	if (stat(tmp_stderr, &st1) != 0 ||
	    stat(tmp_hashname, &st2) != 0 ||
	    rename(tmp_hashname, hashname) != 0 ||
	    rename(tmp_stderr, path_stderr) != 0) {
		cc_log("failed to rename tmp files - %s\n", strerror(errno));
		stats_update(STATS_ERROR);
		failed();
	}

	cc_log("Placed %s into cache\n", output_file);
	stats_tocache(file_size(&st1) + file_size(&st2));

	free(tmp_hashname);
	free(tmp_stderr);
	free(tmp_stdout);
	free(path_stderr);
}
Ejemplo n.º 15
0
/* 
   process the compiler options to form the correct set of options 
   for obtaining the preprocessor output
*/
static void process_args(int argc, char **argv)
{
	int i;
	int found_c_opt = 0;
	int found_S_opt = 0;
	struct stat st;
	char *e;
	/* is gcc being asked to output dependencies? */
	int generating_dependencies = 0;
	/* is the dependency makefile name overridden with -MF? */
	int dependency_filename_specified = 0;
	/* is the dependency makefile target name specified with -MQ or -MF? */
	int dependency_target_specified = 0;


	stripped_args = args_init(0, NULL);

	args_add(stripped_args, argv[0]);

	/* -c not required for SWIG */
	if (swig) {
		found_c_opt = 1;
	}

	for (i=1; i<argc; i++) {
		/* some options will never work ... */
		if (strcmp(argv[i], "-E") == 0) {
			failed();
		}

		/* these are too hard */
		if (strcmp(argv[i], "-fbranch-probabilities")==0 ||
		    strcmp(argv[i], "-fprofile-arcs") == 0 ||
		    strcmp(argv[i], "-ftest-coverage") == 0 ||
		    strcmp(argv[i], "--coverage") == 0 ||
		    strcmp(argv[i], "-M") == 0 ||
		    strcmp(argv[i], "-MM") == 0 ||
		    strcmp(argv[i], "-x") == 0) {
			cc_log("argument %s is unsupported\n", argv[i]);
			stats_update(STATS_UNSUPPORTED);
			failed();
			continue;
		}

		/* we must have -c */
		if (strcmp(argv[i], "-c") == 0) {
			if (!strip_c_option) {
				args_add(stripped_args, argv[i]);
			}
			found_c_opt = 1;
			continue;
		}

		/* -S changes the default extension */
		if (strcmp(argv[i], "-S") == 0) {
			args_add(stripped_args, argv[i]);
			found_S_opt = 1;
			continue;
		}
		
		/* we need to work out where the output was meant to go */
		if (strcmp(argv[i], "-o") == 0) {
			if (i == argc-1) {
				cc_log("missing argument to %s\n", argv[i]);
				stats_update(STATS_ARGS);
				failed();
			}
			output_file = argv[i+1];
			i++;
			continue;
		}
		
		/* alternate form of -o, with no space */
		if (!swig) { /* some of SWIG's arguments begin with -o */
			if (strncmp(argv[i], "-o", 2) == 0) {
				output_file = &argv[i][2];
				continue;
			}
		}

		/* debugging is handled specially, so that we know if we
		   can strip line number info 
		*/
		if (strncmp(argv[i], "-g", 2) == 0) {
			args_add(stripped_args, argv[i]);
			if (strcmp(argv[i], "-g0") != 0) {
				enable_unify = 0;
			}
			continue;
		}

		/* The user knows best: just swallow the next arg */
		if (strcmp(argv[i], "--ccache-skip") == 0) {
			i++;
			if (i == argc) {
				failed();
			}
			args_add(stripped_args, argv[i]);
			continue;
		}

		/* These options require special handling, because they
		   behave differently with gcc -E, when the output
		   file is not specified. */

		if (strcmp(argv[i], "-MD") == 0 || strcmp(argv[i], "-MMD") == 0) {
			generating_dependencies = 1;
		} else if (strcmp(argv[i], "-MF") == 0) {
			dependency_filename_specified = 1;
		} else if (strcmp(argv[i], "-MQ") == 0 || strcmp(argv[i], "-MT") == 0) {
			dependency_target_specified = 1;
		}

		/* the input file is already preprocessed */
		if (swig && strcmp(argv[i], "-nopreprocess") == 0) {
			direct_i_file = 1;
			continue;
		}

		/* options that take an argument */
		{
			const char *opts[] = {"-I", "-include", "-imacros", "-iprefix",
					      "-iwithprefix", "-iwithprefixbefore",
					      "-L", "-D", "-U", "-x", "-MF", 
					      "-MT", "-MQ", "-isystem", "-aux-info",
					      "--param", "-A", "-Xlinker", "-u",
					      "-idirafter", 
					      NULL};
			int j;
			for (j=0;opts[j];j++) {
				if (strcmp(argv[i], opts[j]) == 0) {
					if (i == argc-1) {
						cc_log("missing argument to %s\n", 
						       argv[i]);
						stats_update(STATS_ARGS);
						failed();
					}
						
					args_add(stripped_args, argv[i]);
					args_add(stripped_args, argv[i+1]);
					i++;
					break;
				}
			}
			if (opts[j]) continue;
		}

		/* other options */
		if (argv[i][0] == '-') {
			args_add(stripped_args, argv[i]);
			continue;
		}

		/* if an argument isn't a plain file then assume its
		   an option, not an input file. This allows us to
		   cope better with unusual compiler options */
		if (stat(argv[i], &st) != 0 || !S_ISREG(st.st_mode)) {
			args_add(stripped_args, argv[i]);
			continue;			
		}

		if (input_file) {
			if (check_extension(argv[i], NULL)) {
				cc_log("multiple input files (%s and %s)\n",
				       input_file, argv[i]);
				stats_update(STATS_MULTIPLE);
			} else if (!found_c_opt) {
				cc_log("called for link with %s\n", argv[i]);
				if (strstr(argv[i], "conftest.")) {
					stats_update(STATS_CONFTEST);
				} else {
					stats_update(STATS_LINK);
				}
			} else {
				cc_log("non C/C++ file %s\n", argv[i]);
				stats_update(STATS_NOTC);
			}
			failed();
		}

		input_file = argv[i];
	}

	if (!input_file) {
		cc_log("No input file found\n");
		stats_update(STATS_NOINPUT);
		failed();
	}

	if (swig) {
		i_extension = check_extension(input_file, NULL);
	} else {
		i_extension = check_extension(input_file, &direct_i_file);
	}
	if (i_extension == NULL) {
		cc_log("Not a C/C++ file - %s\n", input_file);
		stats_update(STATS_NOTC);
		failed();
	}

	if (!found_c_opt) {
		cc_log("No -c option found for %s\n", input_file);
		/* I find that having a separate statistic for autoconf tests is useful,
		   as they are the dominant form of "called for link" in many cases */
		if (strstr(input_file, "conftest.")) {
			stats_update(STATS_CONFTEST);
		} else {
			stats_update(STATS_LINK);
		}
		failed();
	}


	/* don't try to second guess the compilers heuristics for stdout handling */
	if (output_file && strcmp(output_file, "-") == 0) {
		stats_update(STATS_OUTSTDOUT);
		failed();
	}

	if (!swig && !output_file) {
		char *p;
		output_file = x_strdup(input_file);
		if ((p = strrchr(output_file, '/'))) {
			output_file = p+1;
		}
		p = strrchr(output_file, '.');
		if (!p || !p[1]) {
			cc_log("badly formed output_file %s\n", output_file);
			stats_update(STATS_ARGS);
			failed();
		}
		p[1] = found_S_opt ? 's' : 'o';
		p[2] = 0;
	}

	/* If dependencies are generated, configure the preprocessor */

	if (generating_dependencies && output_file) {
		if (!dependency_filename_specified) {
			char *default_depfile_name = x_strdup(output_file);
			char *p = strrchr(default_depfile_name, '.');

			if (p) {
				if (strlen(p) < 2) {
					cc_log("badly formed dependency file %s\n", output_file);
					stats_update(STATS_ARGS);
					failed();
					return;
				}
				*p = 0;
			}
			else  {
				int len = p - default_depfile_name;
				
				p = x_malloc(len + 3);
				strncpy(default_depfile_name, p, len - 1);
				free(default_depfile_name);
				default_depfile_name = p;
			}

			strcat(default_depfile_name, ".d");
			args_add(stripped_args, "-MF");
			args_add(stripped_args, default_depfile_name);
		}

		if (!dependency_target_specified) {
			args_add(stripped_args, "-MT");
			args_add(stripped_args, output_file);
		}
	}

	/* cope with -o /dev/null */
	if (output_file && strcmp(output_file,"/dev/null") != 0 && stat(output_file, &st) == 0 && !S_ISREG(st.st_mode)) {
		cc_log("Not a regular file %s\n", output_file);
		stats_update(STATS_DEVICE);
		failed();
	}

	if ((e=getenv("CCACHE_PREFIX"))) {
		char *p = find_executable(e, MYNAME);
		if (!p) {
			cc_log("could not find executable (%s)\n", e);
			stats_update(STATS_ENVIRONMMENT);
			perror(e);
			exit(1);
		}
		args_add_prefix(stripped_args, p);
	}
}
Ejemplo n.º 16
0
/* run the real compiler and put the result in cache */
static void to_cache(ARGS *args)
{
	char *path_stderr;
	char *tmp_stdout, *tmp_stderr, *tmp_outfiles;
	struct stat st1;
	int status;
	int cached_files_count = 0;
	int files_size = 0;

	x_asprintf(&tmp_stdout, "%s/tmp.stdout.%s", temp_dir, tmp_string());
	x_asprintf(&tmp_stderr, "%s/tmp.stderr.%s", temp_dir, tmp_string());
	x_asprintf(&tmp_outfiles, "%s/tmp.outfiles.%s", temp_dir, tmp_string());

	if (strip_c_option && !swig) {
		args_add(stripped_args, "-c");
	}

	if (output_file) {
		args_add(args, "-o");
		args_add(args, output_file);
	}

	/* Turn off DEPENDENCIES_OUTPUT when running cc1, because
	 * otherwise it will emit a line like
	 *
	 *  tmp.stdout.vexed.732.o: /home/mbp/.ccache/tmp.stdout.vexed.732.i
	 *
	 * unsetenv() is on BSD and Linux but not portable. */
	putenv("DEPENDENCIES_OUTPUT");

	/* Give SWIG a filename for it to create and populate with a list of files that it generates */
	if (swig) {
		char *ccache_outfiles;
		x_asprintf(&ccache_outfiles, "CCACHE_OUTFILES=%s", tmp_outfiles);
		unlink(tmp_outfiles);
		if (getenv("CCACHE_OUTFILES") || putenv(ccache_outfiles) == -1) {
			cc_log("CCACHE_OUTFILES env variable already set or could not be set\n");
			stats_update(STATS_ERROR);
			failed();
		}
	}
	
	if (getenv("CCACHE_CPP2")) {
		args_add(args, input_file);
	} else {
		if (swig) {
			args_add(args, "-nopreprocess");
		}
		args_add(args, i_tmpfile);
	}
	status = execute(args->argv, tmp_stdout, tmp_stderr);
	args_pop(args, 3);

	if (stat(tmp_stdout, &st1) != 0 || st1.st_size != 0) {
		cc_log("compiler produced stdout for %s\n", input_file);
		stats_update(STATS_STDOUT);
		unlink(tmp_stdout);
		unlink(tmp_stderr);
		unlink(tmp_outfiles);
		if (!swig) unlink(output_file);
		failed();
	}
	unlink(tmp_stdout);

	if (status != 0) {
		int fd;
		cc_log("compile of %s gave status = %d\n", input_file, status);
		stats_update(STATS_STATUS);

		fd = open(tmp_stderr, O_RDONLY | O_BINARY);
		if (fd != -1) {
			if (cpp_stderr) {
				/* we might have some stderr from cpp */
				int fd2 = open(cpp_stderr, O_RDONLY | O_BINARY);
				if (fd2 != -1) {
					copy_fd(fd2, 2);
					close(fd2);
					unlink(cpp_stderr);
					cpp_stderr = NULL;
				}
			}

			/* we can use a quick method of
			   getting the failed output */
			copy_fd(fd, 2);
			close(fd);
			unlink(tmp_stderr);
			if (i_tmpfile && !direct_i_file) {
				unlink(i_tmpfile);
			}
			exit(status);
		}
		
		unlink(tmp_stderr);
		unlink(tmp_outfiles);
		if (!swig) unlink(output_file);
		failed();
	} else {
		int hardlink = (getenv("CCACHE_NOCOMPRESS") != 0) && (getenv("CCACHE_HARDLINK") != 0);
		if (swig) {
			/* read the list of generated files and copy each of them into the cache */
			FILE *file;
			file = fopen(tmp_outfiles, "r");
			if (file) {
				char out_filename[FILENAME_MAX + 1];
				char out_filename_cache[FILENAME_MAX + 1];
				while (fgets(out_filename, FILENAME_MAX, file)) {
					char *linefeed = strchr(out_filename, '\n');
					if (linefeed) {
						char *potential_cr = linefeed - 1;
						if (potential_cr >= out_filename && *potential_cr == '\r')
						  *potential_cr = 0;
						*linefeed = 0;

						if (cached_files_count == 0) {
							strcpy(out_filename_cache, hashname);
						} else {
							sprintf(out_filename_cache, "%s.%d", hashname, cached_files_count);
						}

						if (commit_to_cache(out_filename, out_filename_cache, hardlink) != 0) {
							fclose(file);
							unlink(tmp_outfiles);
							failed();
						}
						to_cache_stats_helper(&st1, out_filename_cache, tmp_outfiles, &files_size, &cached_files_count);
					} else {
						cached_files_count = 0;
						break;
					}
				}
				fclose(file);
				if (cached_files_count == 0) {
					cc_log("failed to copy output files to cache - internal error\n");
					stats_update(STATS_ERROR);
					unlink(tmp_outfiles);
					failed();
				}

				/* also copy the (uncompressed) file containing the list of generated files into the cache */
				sprintf(out_filename_cache, "%s.outfiles", hashname);
				if (stat(tmp_outfiles, &st1) != 0 ||
					safe_rename(tmp_outfiles, out_filename_cache) != 0) {
					cc_log("failed to copy outfiles file to cache - %s\n", strerror(errno));
					stats_update(STATS_ERROR);
					unlink(tmp_outfiles);
					failed();
				}
				to_cache_stats_helper(&st1, out_filename_cache, tmp_outfiles, &files_size, &cached_files_count);
				unlink(tmp_outfiles);
			} else {
				cc_log("failed to open temp outfiles file - %s\n", strerror(errno));
				stats_update(STATS_ERROR);
				failed();
			}
		} else {
			if (commit_to_cache(output_file, hashname, hardlink) != 0) {
				failed();
			}
			to_cache_stats_helper(&st1, hashname, 0, &files_size, &cached_files_count);
		}
	}

	x_asprintf(&path_stderr, "%s.stderr", hashname);

	if (stat(tmp_stderr, &st1) != 0 ||
		move_file(tmp_stderr, path_stderr) != 0) {
		cc_log("failed to rename tmp files - %s\n", strerror(errno));
		stats_update(STATS_ERROR);
		failed();
	}

	to_cache_stats_helper(&st1, path_stderr, 0, &files_size, &cached_files_count);

	cc_log("Placed %d files for %s into cache\n", cached_files_count, input_file);
	stats_tocache(files_size, cached_files_count);

	free(tmp_stderr);
	free(tmp_stdout);
	free(tmp_outfiles);
	free(path_stderr);
}