blob: 71820bcf7c900b6e42472c8f39b51a9b57e9f4f0 [file] [log] [blame]
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include "Charmonizer/Core/Make.h"
#include "Charmonizer/Core/Compiler.h"
#include "Charmonizer/Core/OperatingSystem.h"
#include "Charmonizer/Core/Util.h"
struct chaz_MakeVar {
char *name;
char *value;
size_t num_elements;
};
struct chaz_MakeRule {
char *targets;
char *prereqs;
char *commands;
};
struct chaz_MakeFile {
chaz_MakeVar **vars;
size_t num_vars;
chaz_MakeRule **rules;
size_t num_rules;
chaz_MakeRule *clean;
chaz_MakeRule *distclean;
};
/* Static vars. */
static struct {
char *make_command;
int shell_type;
} chaz_Make = {
NULL,
0
};
/* Detect make command.
*
* The argument list must be a NULL-terminated series of different spellings
* of `make`, which will be auditioned in the order they are supplied. Here
* are several possibilities:
*
* make
* gmake
* nmake
* dmake
*/
static int
chaz_Make_detect(const char *make1, ...);
static int
chaz_Make_audition(const char *make);
static chaz_MakeRule*
S_new_rule(const char *target, const char *prereq);
static void
S_destroy_rule(chaz_MakeRule *rule);
static void
S_write_rule(chaz_MakeRule *rule, FILE *out);
void
chaz_Make_init(const char *make_command) {
if (make_command) {
chaz_Make.make_command = chaz_Util_strdup(make_command);
}
else {
chaz_Make_detect("make", "gmake", "nmake", "dmake", "mingw32-make",
"mingw64-make", NULL);
}
if (chaz_Make.make_command) {
if (strcmp(chaz_Make.make_command, "nmake") == 0) {
chaz_Make.shell_type = CHAZ_OS_CMD_EXE;
}
else {
/* TODO: Feature test which shell make uses on Windows. */
chaz_Make.shell_type = CHAZ_OS_POSIX;
}
}
}
void
chaz_Make_clean_up(void) {
free(chaz_Make.make_command);
}
const char*
chaz_Make_get_make(void) {
return chaz_Make.make_command;
}
int
chaz_Make_shell_type(void) {
return chaz_Make.shell_type;
}
static int
chaz_Make_detect(const char *make1, ...) {
va_list args;
const char *candidate;
int found = 0;
const char makefile_content[] = "foo:\n\techo \"foo!\"\n";
chaz_Util_write_file("_charm_Makefile", makefile_content);
/* Audition candidates. */
found = chaz_Make_audition(make1);
va_start(args, make1);
while (!found && (NULL != (candidate = va_arg(args, const char*)))) {
found = chaz_Make_audition(candidate);
}
va_end(args);
chaz_Util_remove_and_verify("_charm_Makefile");
return found;
}
static int
chaz_Make_audition(const char *make) {
int succeeded = 0;
char *command = chaz_Util_join(" ", make, "-f", "_charm_Makefile", NULL);
chaz_Util_remove_and_verify("_charm_foo");
chaz_OS_run_redirected(command, "_charm_foo");
if (chaz_Util_can_open_file("_charm_foo")) {
size_t len;
char *content = chaz_Util_slurp_file("_charm_foo", &len);
if (NULL != strstr(content, "foo!")) {
succeeded = 1;
}
free(content);
}
chaz_Util_remove_and_verify("_charm_foo");
if (succeeded) {
chaz_Make.make_command = chaz_Util_strdup(make);
}
free(command);
return succeeded;
}
chaz_MakeFile*
chaz_MakeFile_new() {
chaz_MakeFile *makefile = (chaz_MakeFile*)malloc(sizeof(chaz_MakeFile));
const char *exe_ext = chaz_OS_exe_ext();
const char *obj_ext = chaz_CC_obj_ext();
char *generated;
makefile->vars = (chaz_MakeVar**)malloc(sizeof(chaz_MakeVar*));
makefile->vars[0] = NULL;
makefile->num_vars = 0;
makefile->rules = (chaz_MakeRule**)malloc(sizeof(chaz_MakeRule*));
makefile->rules[0] = NULL;
makefile->num_rules = 0;
makefile->clean = S_new_rule("clean", NULL);
makefile->distclean = S_new_rule("distclean", "clean");
generated = chaz_Util_join("", "charmonizer", exe_ext, " charmonizer",
obj_ext, " charmony.h Makefile", NULL);
chaz_MakeRule_add_rm_command(makefile->distclean, generated);
free(generated);
return makefile;
}
void
chaz_MakeFile_destroy(chaz_MakeFile *makefile) {
size_t i;
for (i = 0; makefile->vars[i]; i++) {
chaz_MakeVar *var = makefile->vars[i];
free(var->name);
free(var->value);
free(var);
}
free(makefile->vars);
for (i = 0; makefile->rules[i]; i++) {
S_destroy_rule(makefile->rules[i]);
}
free(makefile->rules);
S_destroy_rule(makefile->clean);
S_destroy_rule(makefile->distclean);
free(makefile);
}
chaz_MakeVar*
chaz_MakeFile_add_var(chaz_MakeFile *makefile, const char *name,
const char *value) {
chaz_MakeVar *var = (chaz_MakeVar*)malloc(sizeof(chaz_MakeVar));
chaz_MakeVar **vars = makefile->vars;
size_t num_vars = makefile->num_vars + 1;
var->name = chaz_Util_strdup(name);
var->value = chaz_Util_strdup("");
var->num_elements = 0;
if (value) { chaz_MakeVar_append(var, value); }
vars = (chaz_MakeVar**)realloc(vars,
(num_vars + 1) * sizeof(chaz_MakeVar*));
vars[num_vars-1] = var;
vars[num_vars] = NULL;
makefile->vars = vars;
makefile->num_vars = num_vars;
return var;
}
chaz_MakeRule*
chaz_MakeFile_add_rule(chaz_MakeFile *makefile, const char *target,
const char *prereq) {
chaz_MakeRule *rule = S_new_rule(target, prereq);
chaz_MakeRule **rules = makefile->rules;
size_t num_rules = makefile->num_rules + 1;
rules = (chaz_MakeRule**)realloc(rules,
(num_rules + 1) * sizeof(chaz_MakeRule*));
rules[num_rules-1] = rule;
rules[num_rules] = NULL;
makefile->rules = rules;
makefile->num_rules = num_rules;
return rule;
}
chaz_MakeRule*
chaz_MakeFile_clean_rule(chaz_MakeFile *makefile) {
return makefile->clean;
}
chaz_MakeRule*
chaz_MakeFile_distclean_rule(chaz_MakeFile *makefile) {
return makefile->distclean;
}
chaz_MakeRule*
chaz_MakeFile_add_exe(chaz_MakeFile *makefile, const char *exe,
const char *sources, chaz_CFlags *link_flags) {
chaz_CFlags *local_flags = chaz_CC_new_cflags();
const char *link = chaz_CC_link_command();
const char *link_flags_string = "";
const char *local_flags_string;
chaz_MakeRule *rule;
char *command;
rule = chaz_MakeFile_add_rule(makefile, exe, sources);
if (link_flags) {
link_flags_string = chaz_CFlags_get_string(link_flags);
}
if (chaz_CC_msvc_version_num()) {
chaz_CFlags_append(local_flags, "/nologo");
}
chaz_CFlags_set_link_output(local_flags, exe);
local_flags_string = chaz_CFlags_get_string(local_flags);
command = chaz_Util_join(" ", link, sources, link_flags_string,
local_flags_string, NULL);
chaz_MakeRule_add_command(rule, command);
chaz_MakeRule_add_rm_command(makefile->clean, exe);
chaz_CFlags_destroy(local_flags);
free(command);
return rule;
}
chaz_MakeRule*
chaz_MakeFile_add_compiled_exe(chaz_MakeFile *makefile, const char *exe,
const char *sources, chaz_CFlags *cflags) {
chaz_CFlags *local_flags = chaz_CC_new_cflags();
const char *cc = chaz_CC_get_cc();
const char *cflags_string = "";
const char *local_flags_string;
chaz_MakeRule *rule;
char *command;
rule = chaz_MakeFile_add_rule(makefile, exe, sources);
if (cflags) {
cflags_string = chaz_CFlags_get_string(cflags);
}
if (chaz_CC_msvc_version_num()) {
chaz_CFlags_append(local_flags, "/nologo");
}
chaz_CFlags_set_output_exe(local_flags, exe);
local_flags_string = chaz_CFlags_get_string(local_flags);
command = chaz_Util_join(" ", cc, sources, cflags_string,
local_flags_string, NULL);
chaz_MakeRule_add_command(rule, command);
chaz_MakeRule_add_rm_command(makefile->clean, exe);
/* TODO: Clean .obj file on Windows. */
chaz_CFlags_destroy(local_flags);
free(command);
return rule;
}
chaz_MakeRule*
chaz_MakeFile_add_shared_lib(chaz_MakeFile *makefile, chaz_Lib *lib,
const char *sources, chaz_CFlags *link_flags) {
chaz_CFlags *local_flags = chaz_CC_new_cflags();
const char *link = chaz_CC_link_command();
const char *shlib_ext = chaz_OS_shared_lib_ext();
const char *link_flags_string = "";
const char *local_flags_string;
chaz_MakeRule *rule;
char *filename;
char *command;
filename = chaz_Lib_filename(lib);
rule = chaz_MakeFile_add_rule(makefile, filename, sources);
if (link_flags) {
link_flags_string = chaz_CFlags_get_string(link_flags);
}
if (chaz_CC_msvc_version_num()) {
chaz_CFlags_append(local_flags, "/nologo");
}
chaz_CFlags_link_shared_library(local_flags);
if (strcmp(shlib_ext, ".dylib") == 0) {
/* Set temporary install name with full path on Darwin. */
const char *dir_sep = chaz_OS_dir_sep();
char *major_v_name = chaz_Lib_major_version_filename(lib);
char *install_name = chaz_Util_join("", "-install_name $(CURDIR)",
dir_sep, major_v_name, NULL);
chaz_CFlags_append(local_flags, install_name);
free(major_v_name);
free(install_name);
}
chaz_CFlags_set_shared_library_version(local_flags, lib);
chaz_CFlags_set_link_output(local_flags, filename);
local_flags_string = chaz_CFlags_get_string(local_flags);
command = chaz_Util_join(" ", link, sources, link_flags_string,
local_flags_string, NULL);
chaz_MakeRule_add_command(rule, command);
free(command);
chaz_MakeRule_add_rm_command(makefile->clean, filename);
/* Add symlinks. */
if (strcmp(shlib_ext, ".dll") != 0) {
char *major_v_name = chaz_Lib_major_version_filename(lib);
char *no_v_name = chaz_Lib_no_version_filename(lib);
command = chaz_Util_join(" ", "ln -sf", filename, major_v_name, NULL);
chaz_MakeRule_add_command(rule, command);
free(command);
if (strcmp(shlib_ext, ".dylib") == 0) {
command = chaz_Util_join(" ", "ln -sf", filename, no_v_name,
NULL);
}
else {
command = chaz_Util_join(" ", "ln -sf", major_v_name, no_v_name,
NULL);
}
chaz_MakeRule_add_command(rule, command);
free(command);
chaz_MakeRule_add_rm_command(makefile->clean, major_v_name);
chaz_MakeRule_add_rm_command(makefile->clean, no_v_name);
free(major_v_name);
free(no_v_name);
}
if (chaz_CC_msvc_version_num()) {
/* Remove import library and export file under MSVC. */
char *lib_filename = chaz_Lib_implib_filename(lib);
char *exp_filename = chaz_Lib_export_filename(lib);
chaz_MakeRule_add_rm_command(makefile->clean, lib_filename);
chaz_MakeRule_add_rm_command(makefile->clean, exp_filename);
free(lib_filename);
free(exp_filename);
}
chaz_CFlags_destroy(local_flags);
free(filename);
return rule;
}
chaz_MakeRule*
chaz_MakeFile_add_static_lib(chaz_MakeFile *makefile, chaz_Lib *lib,
const char *objects) {
const char *shlib_ext = chaz_OS_shared_lib_ext();
chaz_MakeRule *rule;
char *filename;
char *command;
filename = chaz_Lib_filename(lib);
rule = chaz_MakeFile_add_rule(makefile, filename, objects);
command = chaz_CC_format_archiver_command(filename, objects);
chaz_MakeRule_add_command(rule, command);
free(command);
command = chaz_CC_format_ranlib_command(filename);
if (command) {
chaz_MakeRule_add_command(rule, command);
free(command);
}
chaz_MakeRule_add_rm_command(makefile->clean, filename);
/* Add symlinks. */
if (strcmp(shlib_ext, ".dll") != 0) {
char *major_v_name = chaz_Lib_major_version_filename(lib);
char *no_v_name = chaz_Lib_no_version_filename(lib);
command = chaz_Util_join(" ", "ln -sf", filename, major_v_name, NULL);
chaz_MakeRule_add_command(rule, command);
free(command);
if (strcmp(shlib_ext, ".dylib") == 0) {
command = chaz_Util_join(" ", "ln -sf", filename, no_v_name,
NULL);
}
else {
command = chaz_Util_join(" ", "ln -sf", major_v_name, no_v_name,
NULL);
}
chaz_MakeRule_add_command(rule, command);
free(command);
chaz_MakeRule_add_rm_command(makefile->clean, major_v_name);
chaz_MakeRule_add_rm_command(makefile->clean, no_v_name);
free(major_v_name);
free(no_v_name);
}
free(filename);
return rule;
}
chaz_MakeRule*
chaz_MakeFile_add_lemon_exe(chaz_MakeFile *makefile, const char *dir) {
chaz_CFlags *cflags = chaz_CC_new_cflags();
chaz_MakeRule *rule;
const char *dir_sep = chaz_OS_dir_sep();
const char *exe_ext = chaz_OS_exe_ext();
char *lemon_exe = chaz_Util_join("", dir, dir_sep, "lemon", exe_ext, NULL);
char *lemon_c = chaz_Util_join(dir_sep, dir, "lemon.c", NULL);
chaz_CFlags_enable_optimization(cflags);
chaz_MakeFile_add_var(makefile, "LEMON_EXE", lemon_exe);
rule = chaz_MakeFile_add_compiled_exe(makefile, "$(LEMON_EXE)", lemon_c,
cflags);
chaz_CFlags_destroy(cflags);
free(lemon_c);
free(lemon_exe);
return rule;
}
chaz_MakeRule*
chaz_MakeFile_add_lemon_grammar(chaz_MakeFile *makefile,
const char *base_name) {
char *c_file = chaz_Util_join(".", base_name, "c", NULL);
char *h_file = chaz_Util_join(".", base_name, "h", NULL);
char *y_file = chaz_Util_join(".", base_name, "y", NULL);
char *command = chaz_Util_join(" ", "$(LEMON_EXE) -q", y_file, NULL);
chaz_MakeRule *rule = chaz_MakeFile_add_rule(makefile, c_file, y_file);
chaz_MakeRule *clean_rule = chaz_MakeFile_clean_rule(makefile);
chaz_MakeRule_add_prereq(rule, "$(LEMON_EXE)");
chaz_MakeRule_add_command(rule, command);
chaz_MakeRule_add_rm_command(clean_rule, h_file);
chaz_MakeRule_add_rm_command(clean_rule, c_file);
free(c_file);
free(h_file);
free(y_file);
free(command);
return rule;
}
void
chaz_MakeFile_override_cflags(chaz_MakeFile *makefile, const char *obj,
chaz_CFlags *cflags) {
const char *obj_ext = chaz_CC_obj_ext();
const char *cflags_string = chaz_CFlags_get_string(cflags);
size_t obj_ext_len = strlen(obj_ext);
size_t obj_len = strlen(obj);
size_t base_len;
char *src;
char *command;
chaz_MakeRule *rule;
if (obj_len <= obj_ext_len) {
chaz_Util_die("Invalid object file: %s", obj);
}
base_len = obj_len - obj_ext_len;
if (strcmp(obj + base_len, obj_ext) != 0) {
chaz_Util_die("Invalid object file: %s", obj);
}
src = malloc(base_len + sizeof(".c"));
memcpy(src, obj, base_len);
memcpy(src + base_len, ".c", sizeof(".c"));
rule = chaz_MakeFile_add_rule(makefile, obj, src);
if (chaz_CC_msvc_version_num()) {
command = chaz_Util_join(" ", "$(CC) /nologo", cflags_string, "/c",
src, "/Fo$@", NULL);
}
else {
command = chaz_Util_join(" ", "$(CC)", cflags_string, "-c", src,
"-o $@", NULL);
}
chaz_MakeRule_add_command(rule, command);
free(command);
free(src);
}
chaz_MakeRule*
chaz_MakeFile_add_obj_dir_rule(chaz_MakeFile *makefile, const char *src_dir,
const char *obj_dir, chaz_CFlags *cflags) {
chaz_MakeRule *rule;
const char *dir_sep = chaz_OS_dir_sep();
const char *obj_ext = chaz_CC_obj_ext();
const char *mkdir_command = NULL;
const char *cflags_string = NULL;
char *target = NULL;
char *prereq = NULL;
char *cc_command = NULL;
if (obj_dir == NULL) {
obj_dir = src_dir;
}
if (strcmp(chaz_Make.make_command, "nmake") == 0) {
/* nmake-style search paths in inference rules. */
target = chaz_Util_join("", "{", src_dir, "\\}.c{", obj_dir,
"\\}", obj_ext, NULL);
}
else {
/* GNU make pattern match. TODO: This is not POSIX-compatible. */
target = chaz_Util_join("", obj_dir, dir_sep, "%", obj_ext, NULL);
prereq = chaz_Util_join(dir_sep, src_dir, "%.c", NULL);
}
rule = chaz_MakeFile_add_rule(makefile, target, prereq);
if (chaz_Make.shell_type == CHAZ_OS_POSIX) {
mkdir_command = "@mkdir -p $$(dirname $@)";
}
else if (chaz_Make.shell_type == CHAZ_OS_CMD_EXE) {
mkdir_command = "@for %F in ($@) do @mkdir %~dpF 2>nul";
}
else {
chaz_Util_die("Unsupported shell type: %d", chaz_Make.shell_type);
}
if (cflags == NULL) {
cflags_string = "$(CFLAGS)";
}
else {
cflags_string = chaz_CFlags_get_string(cflags);
}
if (chaz_CC_msvc_version_num()) {
cc_command = chaz_Util_join(" ", "$(CC) /nologo", cflags_string,
"/c $< /Fo$@", NULL);
}
else {
cc_command = chaz_Util_join(" ", "$(CC)", cflags_string, "-c $< -o $@",
NULL);
}
chaz_MakeRule_add_command(rule, mkdir_command);
chaz_MakeRule_add_command(rule, cc_command);
free(cc_command);
free(prereq);
free(target);
return rule;
}
void
chaz_MakeFile_write(chaz_MakeFile *makefile) {
FILE *out;
size_t i;
out = fopen("Makefile", "w");
if (!out) {
chaz_Util_die("Can't open Makefile\n");
}
for (i = 0; makefile->vars[i]; i++) {
chaz_MakeVar *var = makefile->vars[i];
fprintf(out, "%s = %s\n", var->name, var->value);
}
fprintf(out, "\n");
for (i = 0; makefile->rules[i]; i++) {
S_write_rule(makefile->rules[i], out);
}
S_write_rule(makefile->clean, out);
S_write_rule(makefile->distclean, out);
/* Suffix rule for .c files. */
if (chaz_CC_msvc_version_num()) {
fprintf(out, ".c.obj :\n");
fprintf(out, "\t$(CC) /nologo $(CFLAGS) /c $< /Fo$@\n\n");
}
else {
fprintf(out, ".c.o :\n");
fprintf(out, "\t$(CC) $(CFLAGS) -c $< -o $@\n\n");
}
fclose(out);
}
void
chaz_MakeVar_append(chaz_MakeVar *var, const char *element) {
char *value;
if (element[0] == '\0') { return; }
if (var->num_elements == 0) {
value = chaz_Util_strdup(element);
}
else {
value = (char*)malloc(strlen(var->value) + strlen(element) + 20);
if (var->num_elements == 1) {
sprintf(value, "\\\n %s \\\n %s", var->value, element);
}
else {
sprintf(value, "%s \\\n %s", var->value, element);
}
}
free(var->value);
var->value = value;
var->num_elements++;
}
static chaz_MakeRule*
S_new_rule(const char *target, const char *prereq) {
chaz_MakeRule *rule = (chaz_MakeRule*)malloc(sizeof(chaz_MakeRule));
rule->targets = NULL;
rule->prereqs = NULL;
rule->commands = NULL;
if (target) { chaz_MakeRule_add_target(rule, target); }
if (prereq) { chaz_MakeRule_add_prereq(rule, prereq); }
return rule;
}
static void
S_destroy_rule(chaz_MakeRule *rule) {
if (rule->targets) { free(rule->targets); }
if (rule->prereqs) { free(rule->prereqs); }
if (rule->commands) { free(rule->commands); }
free(rule);
}
static void
S_write_rule(chaz_MakeRule *rule, FILE *out) {
fprintf(out, "%s :", rule->targets);
if (rule->prereqs) {
fprintf(out, " %s", rule->prereqs);
}
fprintf(out, "\n");
if (rule->commands) {
fprintf(out, "%s", rule->commands);
}
fprintf(out, "\n");
}
void
chaz_MakeRule_add_target(chaz_MakeRule *rule, const char *target) {
char *targets;
if (!rule->targets) {
targets = chaz_Util_strdup(target);
}
else {
targets = chaz_Util_join(" ", rule->targets, target, NULL);
free(rule->targets);
}
rule->targets = targets;
}
void
chaz_MakeRule_add_prereq(chaz_MakeRule *rule, const char *prereq) {
char *prereqs;
if (!rule->prereqs) {
prereqs = chaz_Util_strdup(prereq);
}
else {
prereqs = chaz_Util_join(" ", rule->prereqs, prereq, NULL);
free(rule->prereqs);
}
rule->prereqs = prereqs;
}
void
chaz_MakeRule_add_command(chaz_MakeRule *rule, const char *command) {
char *commands;
if (!rule->commands) {
commands = (char*)malloc(strlen(command) + 20);
sprintf(commands, "\t%s\n", command);
}
else {
commands = (char*)malloc(strlen(rule->commands) + strlen(command) + 20);
sprintf(commands, "%s\t%s\n", rule->commands, command);
free(rule->commands);
}
rule->commands = commands;
}
void
chaz_MakeRule_add_command_with_libpath(chaz_MakeRule *rule,
const char *command, ...) {
va_list args;
char *path = NULL;
char *lib_command = NULL;
if (strcmp(chaz_OS_shared_lib_ext(), ".so") == 0) {
va_start(args, command);
path = chaz_Util_vjoin(":", args);
va_end(args);
lib_command = chaz_Util_join("", "LD_LIBRARY_PATH=", path,
":$$LD_LIBRARY_PATH ", command, NULL);
free(path);
}
else if (strcmp(chaz_OS_shared_lib_ext(), ".dll") == 0) {
va_start(args, command);
path = chaz_Util_vjoin(";", args);
va_end(args);
lib_command = chaz_Util_join("", "path ", path, ";%path% && ", command,
NULL);
}
else {
/* Assume that library paths are compiled into the executable on
* Darwin.
*/
lib_command = chaz_Util_strdup(command);
}
chaz_MakeRule_add_command(rule, lib_command);
free(lib_command);
}
void
chaz_MakeRule_add_rm_command(chaz_MakeRule *rule, const char *files) {
char *command;
if (chaz_Make.shell_type == CHAZ_OS_POSIX) {
command = chaz_Util_join(" ", "rm -f", files, NULL);
}
else if (chaz_Make.shell_type == CHAZ_OS_CMD_EXE) {
command = chaz_Util_join("", "for %i in (", files,
") do @if exist %i del /f %i", NULL);
}
else {
chaz_Util_die("Unsupported shell type: %d", chaz_Make.shell_type);
}
chaz_MakeRule_add_command(rule, command);
free(command);
}
void
chaz_MakeRule_add_recursive_rm_command(chaz_MakeRule *rule, const char *dirs) {
char *command;
if (chaz_Make.shell_type == CHAZ_OS_POSIX) {
command = chaz_Util_join(" ", "rm -rf", dirs, NULL);
}
else if (chaz_Make.shell_type == CHAZ_OS_CMD_EXE) {
command = chaz_Util_join("", "for %i in (", dirs,
") do @if exist %i rmdir /s /q %i", NULL);
}
else {
chaz_Util_die("Unsupported shell type: %d", chaz_Make.shell_type);
}
chaz_MakeRule_add_command(rule, command);
free(command);
}
void
chaz_MakeRule_add_make_command(chaz_MakeRule *rule, const char *dir,
const char *target) {
char *command;
if (chaz_Make.shell_type == CHAZ_OS_POSIX) {
if (!target) {
command = chaz_Util_join("", "(cd ", dir, " && $(MAKE))", NULL);
}
else {
command = chaz_Util_join("", "(cd ", dir, " && $(MAKE) ", target,
")", NULL);
}
chaz_MakeRule_add_command(rule, command);
free(command);
}
else if (chaz_Make.shell_type == CHAZ_OS_CMD_EXE) {
if (!target) {
command = chaz_Util_join(" ", "pushd", dir, "&& $(MAKE) && popd",
NULL);
}
else {
command = chaz_Util_join(" ", "pushd", dir, "&& $(MAKE)", target,
"&& popd", NULL);
}
chaz_MakeRule_add_command(rule, command);
free(command);
}
else {
chaz_Util_die("Unsupported shell type: %d", chaz_Make.shell_type);
}
}
void
chaz_Make_list_files(const char *dir, const char *ext,
chaz_Make_list_files_callback_t callback, void *context) {
int shell_type = chaz_OS_shell_type();
const char *pattern;
char *command;
char *list;
char *prefix;
char *file;
size_t command_size;
size_t list_len;
size_t prefix_len;
/* List files using shell. */
if (shell_type == CHAZ_OS_POSIX) {
pattern = "find %s -name '*.%s' -type f";
}
else if (shell_type == CHAZ_OS_CMD_EXE) {
pattern = "dir %s\\*.%s /s /b /a-d";
}
else {
chaz_Util_die("Unknown shell type %d", shell_type);
}
command_size = strlen(pattern) + strlen(dir) + strlen(ext) + 10;
command = (char*)malloc(command_size);
sprintf(command, pattern, dir, ext);
list = chaz_OS_run_and_capture(command, &list_len);
free(command);
if (!list) {
chaz_Util_die("Failed to list files in '%s'", dir);
}
list[list_len-1] = 0;
/* Find directory prefix to strip from files */
if (shell_type == CHAZ_OS_POSIX) {
prefix_len = strlen(dir);
prefix = (char*)malloc(prefix_len + 2);
memcpy(prefix, dir, prefix_len);
prefix[prefix_len++] = '/';
prefix[prefix_len] = '\0';
}
else {
char *output;
size_t output_len;
/* 'dir /s' returns absolute paths, so we have to find the absolute
* path of the directory. This is done by using the variable
* substitution feature of the 'for' command.
*/
pattern = "for %%I in (%s) do @echo %%~fI";
command_size = strlen(pattern) + strlen(dir) + 10;
command = (char*)malloc(command_size);
sprintf(command, pattern, dir);
output = chaz_OS_run_and_capture(command, &output_len);
free(command);
if (!output) { chaz_Util_die("Failed to find absolute path"); }
/* Strip whitespace from end of output. */
for (prefix_len = output_len; prefix_len > 0; --prefix_len) {
if (!isspace(output[prefix_len-1])) { break; }
}
prefix = (char*)malloc(prefix_len + 2);
memcpy(prefix, output, prefix_len);
prefix[prefix_len++] = '\\';
prefix[prefix_len] = '\0';
free(output);
}
/* Iterate file list and invoke callback. */
for (file = strtok(list, "\r\n"); file; file = strtok(NULL, "\r\n")) {
if (strlen(file) <= prefix_len
|| memcmp(file, prefix, prefix_len) != 0
) {
chaz_Util_die("Expected prefix '%s' for file name '%s'", prefix,
file);
}
callback(dir, file + prefix_len, context);
}
free(prefix);
free(list);
}