From 9bbdb791afb826a2786c0fa103e524a9e10fa90c Mon Sep 17 00:00:00 2001 From: Jonas 'Sortie' Termansen Date: Mon, 26 Dec 2016 13:59:51 +0100 Subject: [PATCH] Add split packages and cross-bootstrapping support to tix-build(8). pkg.use-bootstrap can now be set to true to add a bootstrap phase to cross-builds. I.e. the package is built for the native platform and installed to a temporary location, which is in the PATH during the actual cross-compilation. This feature is useful for some misbehaving ports that can cross-compile, but require the exact same version of the software installed locally. The bootstrap build is controlled with the bootstrap.foo variables rather than the normal pkg.foo variables. pkg.source-package can now be set to the name of another package, whose source code is built using the current tixbuildinfo. This feature allows providing multiple packages using the same source code package. By default, the source code of the source package is assumed to be in ../${pkg.source-package}, but this can be overridden with the option --source-directory. pkg.alias-of can now be set to the name of another package to specify that this package is an alias of the other package, creating an empty binary package depending on the real package. pkg.subdir support has been fixed in the clean and post-install phases. pkg-config support has been improved and PKG_CONFIG is now set to $HOST-pkg-config and PKG_CONFIG_FOR_BUILD is set to pkg-config. tix-build has been refactored as needed and generally cleaned up. Error handling, such as on allocations, have been added in a lot of cases. The support for FOO_FOR_BUILD variables have been unified and simplified. Appending to PATH now correctly handles the empty PATH. --- build-aux/build-ports.sh | 7 +- share/man/man7/following-development.7 | 39 + tix/tix-build.c | 1214 ++++++++++++++---------- tix/tix.c | 2 + 4 files changed, 768 insertions(+), 494 deletions(-) diff --git a/build-aux/build-ports.sh b/build-aux/build-ports.sh index 083e3844..13922ea0 100755 --- a/build-aux/build-ports.sh +++ b/build-aux/build-ports.sh @@ -84,8 +84,8 @@ fi get_package_dependencies_raw() {( PACKAGE_DIR=$(echo $1 | grep -Eo '^[^\.]*') ! [ -f "$SORTIX_PORTS_DIR/$PACKAGE_DIR/tixbuildinfo" ] || - grep -E "^pkg\.build-libraries=.*" "$SORTIX_PORTS_DIR/$PACKAGE_DIR/tixbuildinfo" | \ - sed 's/^pkg\.build-libraries=//' + grep -E "^(pkg\.build-libraries|pkg\.alias-of)=.*" "$SORTIX_PORTS_DIR/$PACKAGE_DIR/tixbuildinfo" | \ + sed 's/^[^=]*=//' )} # Detect the build-time dependencies for a package with missing optional @@ -151,12 +151,15 @@ strip_tix() { # Build all the packages (if needed) and otherwise install them. for PACKAGE in $PACKAGES; do if ! [ -f "$SORTIX_REPOSITORY_DIR/$PACKAGE.tix.tar.xz" ]; then + SOURCE_PACKAGE=$(grep -E "^pkg.source-package=" "$SORTIX_PORTS_DIR/$PACKAGE/tixbuildinfo" | \ + sed 's/^[^=]*=//') tix-build \ --sysroot="$SYSROOT" \ --host=$HOST \ --prefix= \ --destination="$SORTIX_REPOSITORY_DIR" \ --generation=2 \ + ${SOURCE_PACKAGE:+--source-package "$SORTIX_PORTS_DIR/$SOURCE_PACKAGE"} \ "$SORTIX_PORTS_DIR/$PACKAGE" strip_tix "$SORTIX_REPOSITORY_DIR/$PACKAGE.tix.tar.xz" fi diff --git a/share/man/man7/following-development.7 b/share/man/man7/following-development.7 index 788cb1ba..d3c9212c 100644 --- a/share/man/man7/following-development.7 +++ b/share/man/man7/following-development.7 @@ -59,6 +59,45 @@ releasing Sortix x.y, foo." to allow the maintainer to easily .Xr grep 1 for it after a release. .Sh CHANGES +.Ss Add split packages and cross-bootstrapping support to tix-build(8) +.Xr tix-build 8 +has gained a number of features that will soon be required in order to build +certain ports. In particular, it now supports the +.Sy pkg.use-bootstrap , +.Sy pkg.source-package , +and +.Sy pkg.alias-of +variables; bugs in the +.Sy pkg.subdir +variable have been fixed; and a +.Fl \-source-directory +option has been added. +.Xr tix-build 8 +must be upgraded before building ports using any of those feature. +.Bd -literal + cd /src/tix && + make clean && + make install +.Ed +.Pp +If not developing natively, set +.Ev PREFIX +to the desired location. +.Pp +If the new program isn't used, ports may fail to build due to local software not +being the exact same version +.Sy ( pkg.use-bootstrap ) , +clean or post-install in the wrong subdirectory +.Sy ( pkg.subdir) , +the +.Fl \-source-directory +option not being recognized or failing to locate the source code +.Sy ( pkg.source-package) , +stopping because +.Sy pkg.build-system +isn't set +.Sy ( pkg.alias-of) , +or other mysterious circumstances. .Ss Add German keyboard layout. The .Xr kblayout-compiler 1 diff --git a/tix/tix-build.c b/tix/tix-build.c index faf629aa..202e3705 100644 --- a/tix/tix-build.c +++ b/tix/tix-build.c @@ -39,6 +39,37 @@ #include "util.h" +struct buildvar +{ + const char* variable; + const char* value; + bool program; +}; + +static struct buildvar buildvars[] = +{ + { "AR", "ar", true }, + { "AS", "as", true }, + { "CC", "gcc", true }, + { "CFLAGS", "-Os", false }, + { "CPP", "gcc -E", true }, + { "CPP", "", false }, + { "CXXFILT", "c++filt", true }, + { "CXX", "g++", true }, + { "CXXFLAGS", "-Os", false }, + { "LD", "ld", true }, + { "LDFLAGS", "", false }, + { "NM", "nm", true }, + { "OBJCOPY", "objcopy", true }, + { "OBJDUMP", "objdump", true }, + { "PKG_CONFIG", "pkg-config", true }, + { "RANLIB", "ranlib", true }, + { "READELF", "readelf", true }, + { "STRIP", "strip", true }, +}; + +#define BUILDVARS_LENGTH (sizeof(buildvars) / sizeof(buildvars[0])) + enum build_step { BUILD_STEP_NO_SUCH_STEP, @@ -48,14 +79,14 @@ enum build_step BUILD_STEP_BUILD, BUILD_STEP_INSTALL, BUILD_STEP_POST_INSTALL, - BUILD_STEP_PACKAGE, BUILD_STEP_POST_CLEAN, + BUILD_STEP_PACKAGE, BUILD_STEP_END, }; -bool should_do_build_step(enum build_step step, - enum build_step start, - enum build_step end) +static bool should_do_build_step(enum build_step step, + enum build_step start, + enum build_step end) { return start <= step && step <= end; } @@ -63,7 +94,7 @@ bool should_do_build_step(enum build_step step, #define SHOULD_DO_BUILD_STEP(step, minfo) \ should_do_build_step((step), (minfo)->start_step, (minfo)->end_step) -enum build_step step_of_step_name(const char* step_name) +static enum build_step step_of_step_name(const char* step_name) { if ( !strcmp(step_name, "start") ) return BUILD_STEP_START; @@ -88,7 +119,7 @@ enum build_step step_of_step_name(const char* step_name) return BUILD_STEP_NO_SUCH_STEP; } -typedef struct +struct metainfo { char* build; char* build_dir; @@ -109,9 +140,11 @@ typedef struct string_array_t package_info; enum build_step start_step; enum build_step end_step; -} metainfo_t; + bool bootstrapping; + bool cross; +}; -bool has_in_path(const char* program) +static bool has_in_path(const char* program) { pid_t child_pid = fork(); if ( child_pid < 0 ) @@ -122,22 +155,24 @@ bool has_in_path(const char* program) waitpid(child_pid, &exitstatus, 0); return WIFEXITED(exitstatus) && WEXITSTATUS(exitstatus) == 0; } - close(0); open("/dev/null", O_RDONLY); - close(1); open("/dev/null", O_WRONLY); - close(2); open("/dev/null", O_WRONLY); - char* argv[] = + close(0); + close(1); + close(2); + if ( open("/dev/null", O_RDONLY) != 0 || + open("/dev/null", O_WRONLY) != 1 || + open("/dev/null", O_WRONLY) != 2 ) { - (char*) "which", - (char*) program, - (char*) NULL, - }; - execvp(argv[0], argv); + warn("/dev/null"); + _exit(1); + } + const char* argv[] = { "which", program, NULL }; + execvp(argv[0], (char**) argv); _exit(1); } -void emit_compiler_wrapper_invocation(FILE* wrapper, - metainfo_t* minfo, - const char* name) +static void emit_compiler_wrapper_invocation(FILE* wrapper, + struct metainfo* minfo, + const char* name) { fprintf(wrapper, "%s", name); if ( minfo->sysroot ) @@ -145,69 +180,19 @@ void emit_compiler_wrapper_invocation(FILE* wrapper, fprintf(wrapper, " \"$@\""); } -void emit_compiler_warning_wrapper(metainfo_t* minfo, - const char* bindir, - const char* name) -{ - if ( !minfo->sysroot ) - return; - if ( !has_in_path(name) ) - return; - const char* warnings_dir = getenv("TIX_WARNINGS_DIR"); - char* wrapper_path = print_string("%s/%s", bindir, name); - FILE* wrapper = fopen(wrapper_path, "w"); - if ( !wrapper ) - err(1, "`%s'", wrapper_path); - // TODO: Find a portable shell way of doing this. - fprintf(wrapper, "#!/bin/bash\n"); - fprint_shell_variable_assignment(wrapper, "PATH", getenv("PATH")); - fprint_shell_variable_assignment(wrapper, "TIX_WARNINGS_DIR", warnings_dir); - if ( minfo->sysroot ) - fprint_shell_variable_assignment(wrapper, "SYSROOT", minfo->sysroot); - fprintf(wrapper, "warnfile=$(mktemp --tmpdir=\"$TIX_WARNINGS_DIR\")\n"); - fprintf(wrapper, "("); - emit_compiler_wrapper_invocation(wrapper, minfo, name); - fprintf(wrapper, ") 2> >(tee $warnfile >&2)\n"); - fprintf(wrapper, "exitstatus=$?\n"); - fprintf(wrapper, "if test -s \"$warnfile\"; then\n"); - fprintf(wrapper, " if test $exitstatus = 0; then\n"); - fprintf(wrapper, " (echo \"cd $(pwd) && "); - emit_compiler_wrapper_invocation(wrapper, minfo, name); - fprintf(wrapper, " && cat \"$warnfile\") > \"$warnfile.warn\"\n"); - fprintf(wrapper, " else\n"); - fprintf(wrapper, " (echo \"cd $(pwd) && "); - emit_compiler_wrapper_invocation(wrapper, minfo, name); - fprintf(wrapper, " && cat \"$warnfile\") > \"$warnfile.err\"\n"); - fprintf(wrapper, " fi\n"); - fprintf(wrapper, "fi\n"); - fprintf(wrapper, "rm -f \"$warnfile\"\n"); - fprintf(wrapper, "exit $exitstatus\n"); - fflush(wrapper); - fchmod_plus_x(fileno(wrapper)); - fclose(wrapper); - - free(wrapper_path); -} - -void emit_compiler_warning_cross_wrapper(metainfo_t* minfo, - const char* bindir, - const char* name) -{ - char* cross_name = print_string("%s-%s", minfo->host, name); - emit_compiler_warning_wrapper(minfo, bindir, cross_name); - free(cross_name); -} - -void emit_compiler_sysroot_wrapper(metainfo_t* minfo, - const char* bindir, - const char* name) +static void emit_compiler_sysroot_wrapper(struct metainfo* minfo, + const char* bindir, + const char* name) { if ( !has_in_path(name) ) return; - char* wrapper_path = print_string("%s/%s", bindir, name); + char* wrapper_path = join_paths(bindir, name); + if ( !wrapper_path ) + err(1, "malloc"); FILE* wrapper = fopen(wrapper_path, "w"); if ( !wrapper ) err(1, "`%s'", wrapper_path); + fprintf(wrapper, "#!/bin/sh\n"); fprint_shell_variable_assignment(wrapper, "PATH", getenv("PATH")); if ( minfo->sysroot ) fprint_shell_variable_assignment(wrapper, "SYSROOT", minfo->sysroot); @@ -216,135 +201,169 @@ void emit_compiler_sysroot_wrapper(metainfo_t* minfo, fprintf(wrapper, "\n"); fflush(wrapper); fchmod_plus_x(fileno(wrapper)); + if ( ferror(wrapper) || fflush(wrapper) == EOF ) + err(1, "%s", wrapper_path); fclose(wrapper); - free(wrapper_path); } -void emit_compiler_sysroot_cross_wrapper(metainfo_t* minfo, - const char* bindir, - const char* name) +static void emit_compiler_sysroot_cross_wrapper(struct metainfo* minfo, + const char* bindir, + const char* name) { char* cross_name = print_string("%s-%s", minfo->host, name); + if ( !cross_name ) + err(1, "malloc"); emit_compiler_sysroot_wrapper(minfo, bindir, cross_name); free(cross_name); } -void emit_pkg_config_wrapper(metainfo_t* minfo) +static void emit_pkg_config_wrapper(struct metainfo* minfo, const char* bindir) { - char* bindir = print_string("%s/bin", tmp_root); - if ( mkdir(bindir, 0777) < 0 ) - err(1, "mkdir: %s", bindir); - // Create a pkg-config script for the build system. - char* pkg_config_for_build_path = print_string("%s/build-pkg-config", bindir); + char* pkg_config_for_build_path = join_paths(bindir, "pkg-config"); + if ( !pkg_config_for_build_path ) + err(1, "malloc"); FILE* pkg_config_for_build = fopen(pkg_config_for_build_path, "w"); if ( !pkg_config_for_build ) err(1, "`%s'", pkg_config_for_build_path); fprintf(pkg_config_for_build, "#!/bin/sh\n"); - fprint_shell_variable_assignment(pkg_config_for_build, "PATH", getenv("PATH")); - fprint_shell_variable_assignment(pkg_config_for_build, "PKG_CONFIG", getenv("PKG_CONFIG")); - fprint_shell_variable_assignment(pkg_config_for_build, "PKG_CONFIG_PATH", getenv("PKG_CONFIG_PATH")); - fprint_shell_variable_assignment(pkg_config_for_build, "PKG_CONFIG_SYSROOT_DIR", getenv("PKG_CONFIG_SYSROOT_DIR")); - fprint_shell_variable_assignment(pkg_config_for_build, "PKG_CONFIG_FOR_BUILD", getenv("PKG_CONFIG_FOR_BUILD")); - fprint_shell_variable_assignment(pkg_config_for_build, "PKG_CONFIG_LIBDIR", getenv("PKG_CONFIG_LIBDIR")); - fprintf(pkg_config_for_build, "exec ${PKG_CONFIG:-pkg-config} \"$@\"\n"); - fflush(pkg_config_for_build); + fprint_shell_variable_assignment(pkg_config_for_build, + "PATH", getenv("PATH")); + fprint_shell_variable_assignment(pkg_config_for_build, + "PKG_CONFIG", getenv("PKG_CONFIG")); + fprint_shell_variable_assignment(pkg_config_for_build, + "PKG_CONFIG_FOR_BUILD", getenv("PKG_CONFIG_FOR_BUILD")); + if ( getenv("PKG_CONFIG_PATH_FOR_BUILD") ) + fprint_shell_variable_assignment(pkg_config_for_build, + "PKG_CONFIG_PATH", getenv("PKG_CONFIG_PATH_FOR_BUILD")); + else + fprint_shell_variable_assignment(pkg_config_for_build, + "PKG_CONFIG_PATH", getenv("PKG_CONFIG_PATH")); + if ( getenv("PKG_CONFIG_SYSROOT_DIR_FOR_BUILD") ) + fprint_shell_variable_assignment(pkg_config_for_build, + "PKG_CONFIG_SYSROOT_DIR", + getenv("PKG_CONFIG_SYSROOT_DIR_FOR_BUILD")); + else + fprint_shell_variable_assignment(pkg_config_for_build, + "PKG_CONFIG_SYSROOT_DIR", getenv("PKG_CONFIG_SYSROOT_DIR")); + if ( getenv("PKG_CONFIG_LIBDIR_FOR_BUILD") ) + fprint_shell_variable_assignment(pkg_config_for_build, + "PKG_CONFIG_LIBDIR", getenv("PKG_CONFIG_LIBDIR_FOR_BUILD")); + else + fprint_shell_variable_assignment(pkg_config_for_build, + "PKG_CONFIG_LIBDIR", getenv("PKG_CONFIG_LIBDIR")); + fprintf(pkg_config_for_build, + "exec ${PKG_CONFIG_FOR_BUILD:-${PKG_CONFIG:-pkg-config}} \"$@\"\n"); + if ( ferror(pkg_config_for_build) || fflush(pkg_config_for_build) == EOF ) + err(1, "%s", pkg_config_for_build_path); fchmod_plus_x(fileno(pkg_config_for_build)); fclose(pkg_config_for_build); free(pkg_config_for_build_path); // Create a pkg-config script for the host system. - char* pkg_config_path = print_string("%s/pkg-config", bindir); + char* var_pkg_config_libdir = + print_string("%s%s/lib/pkgconfig", + minfo->sysroot, minfo->exec_prefix); + if ( !var_pkg_config_libdir ) + err(1, "malloc"); + char* var_pkg_config_path = strdup(var_pkg_config_libdir); + if ( !var_pkg_config_path ) + err(1, "malloc"); + char* var_pkg_config_sysroot_dir = strdup(minfo->sysroot); + if ( !var_pkg_config_sysroot_dir ) + err(1, "malloc"); + char* pkg_config_name = print_string("%s-pkg-config", minfo->host); + if ( !pkg_config_name ) + err(1, "malloc"); + char* pkg_config_path = join_paths(bindir, pkg_config_name); + if ( !pkg_config_path ) + err(1, "malloc"); FILE* pkg_config = fopen(pkg_config_path, "w"); if ( !pkg_config ) err(1, "`%s'", pkg_config_path); fprintf(pkg_config, "#!/bin/sh\n"); fprint_shell_variable_assignment(pkg_config, "PATH", getenv("PATH")); - fprint_shell_variable_assignment(pkg_config, "PKG_CONFIG", getenv("PKG_CONFIG")); - fprintf(pkg_config, "exec ${PKG_CONFIG:-pkg-config} --static \"$@\"\n"); + fprint_shell_variable_assignment(pkg_config, + "PKG_CONFIG", getenv("PKG_CONFIG")); + fprint_shell_variable_assignment(pkg_config, + "PKG_CONFIG_PATH", var_pkg_config_path); + fprint_shell_variable_assignment(pkg_config, + "PKG_CONFIG_SYSROOT_DIR", var_pkg_config_sysroot_dir); + fprint_shell_variable_assignment(pkg_config, + "PKG_CONFIG_LIBDIR", var_pkg_config_libdir); + // Pass --static as Sortix only static links at the moment. + fprintf(pkg_config, "exec ${PKG_CONFIG:-%s} --static \"$@\"\n", + has_in_path(pkg_config_name) ? pkg_config_name : "pkg-config"); fflush(pkg_config); + if ( ferror(pkg_config) || fflush(pkg_config) == EOF ) + err(1, "%s", pkg_config_path); fchmod_plus_x(fileno(pkg_config)); fclose(pkg_config); free(pkg_config_path); - - // Point to the correct pkg-config configuration through the environment. - char* var_pkg_config = print_string("%s/pkg-config", bindir); - char* var_pkg_config_for_build = print_string("%s/build-pkg-config", bindir); - char* var_pkg_config_libdir = - print_string("%s%s/lib/pkgconfig", - minfo->sysroot, minfo->exec_prefix); - char* var_pkg_config_path = print_string("%s", var_pkg_config_libdir); - char* var_pkg_config_sysroot_dir = print_string("%s", minfo->sysroot); - setenv("PKG_CONFIG", var_pkg_config, 1); - setenv("PKG_CONFIG_FOR_BUILD", var_pkg_config_for_build, 1); - setenv("PKG_CONFIG_LIBDIR", var_pkg_config_libdir, 1); - setenv("PKG_CONFIG_PATH", var_pkg_config_path, 1); - setenv("PKG_CONFIG_SYSROOT_DIR", var_pkg_config_sysroot_dir, 1); - free(var_pkg_config); - free(var_pkg_config_for_build); free(var_pkg_config_libdir); free(var_pkg_config_path); free(var_pkg_config_sysroot_dir); +} - if ( getenv("TIX_WARNINGS_DIR") ) +static void append_to_path(const char* directory) +{ + const char* path = getenv("PATH"); + if ( path && path[0] ) { - char* warnings_dir = print_string("%s/%s", getenv("TIX_WARNINGS_DIR"), minfo->package_name); - if ( mkdir(warnings_dir, 0755) == 0 || errno == EEXIST ) - { - setenv("TIX_WARNINGS_DIR", warnings_dir, 1); - } - else - { - err(1, "mkdir: `%s': compiler warnings won't be saved", warnings_dir); - unsetenv("TIX_WARNINGS_DIR"); - } - free(warnings_dir); + char* new_path = print_string("%s:%s", directory, path); + if ( !new_path ) + err(1, "malloc"); + if ( setenv("PATH", new_path, 1) < 0 ) + err(1, "setenv"); + free(new_path); } + else if ( setenv("PATH", directory, 1) < 0 ) + err(1, "setenv"); +} +static void EmitWrappers(struct metainfo* minfo) +{ + if ( !minfo->cross ) + return; + + char* bindir = join_paths(tmp_root, "bin"); + if ( mkdir(bindir, 0777) < 0 ) + err(1, "mkdir: %s", bindir); + + emit_pkg_config_wrapper(minfo, bindir); emit_compiler_sysroot_cross_wrapper(minfo, bindir, "cc"); emit_compiler_sysroot_cross_wrapper(minfo, bindir, "gcc"); emit_compiler_sysroot_cross_wrapper(minfo, bindir, "c++"); emit_compiler_sysroot_cross_wrapper(minfo, bindir, "g++"); emit_compiler_sysroot_cross_wrapper(minfo, bindir, "ld"); - if ( getenv("TIX_WARNINGS_DIR") ) - { - emit_compiler_warning_wrapper(minfo, bindir, "cc"); - emit_compiler_warning_wrapper(minfo, bindir, "gcc"); - emit_compiler_warning_wrapper(minfo, bindir, "c++"); - emit_compiler_warning_wrapper(minfo, bindir, "g++"); - - emit_compiler_warning_cross_wrapper(minfo, bindir, "cc"); - emit_compiler_warning_cross_wrapper(minfo, bindir, "gcc"); - emit_compiler_warning_cross_wrapper(minfo, bindir, "c++"); - emit_compiler_warning_cross_wrapper(minfo, bindir, "g++"); - } - - char* new_path = print_string("%s:%s", bindir, getenv("PATH") ? getenv("PATH") : ""); - setenv("PATH", new_path, 1); - free(new_path); + append_to_path(bindir); free(bindir); } -void SetNeedVariableBuildTool(metainfo_t* minfo, - const char* variable, - const char* value) +static void SetNeedVariableBuildTool(struct metainfo* minfo, + const char* variable, + const char* value) { string_array_t* pkg_info = &minfo->package_info; - const char* needed_vars = dictionary_get_def(pkg_info, "pkg.make.needed-vars", "true"); + const char* needed_vars = + dictionary_get_def(pkg_info, "pkg.make.needed-vars", "true"); char* key = print_string("pkg.make.needed-vars.%s", variable); + if ( !key ) + err(1, "malloc"); const char* needed_var = dictionary_get_def(pkg_info, key, needed_vars); free(key); if ( !parse_boolean(needed_var) ) return; - setenv(variable, value, 1); + if ( setenv(variable, value, 1) < 0 ) + err(1, "setenv"); } -void SetNeedVariableCrossTool(metainfo_t* minfo, - const char* variable, - const char* value) +static void SetNeedVariableCrossTool(struct metainfo* minfo, + const char* variable, + const char* value) { if ( strcmp(minfo->build, minfo->host) == 0 ) { @@ -353,140 +372,150 @@ void SetNeedVariableCrossTool(metainfo_t* minfo, else { char* newvalue = print_string("%s-%s", minfo->host, value); + if ( !newvalue ) + err(1, "malloc"); SetNeedVariableBuildTool(minfo, variable, newvalue); free(newvalue); } } -void SetNeededVariables(metainfo_t* minfo) +static void SetNeededVariables(struct metainfo* minfo) { - SetNeedVariableBuildTool(minfo, "AR_FOR_BUILD", "ar"); - SetNeedVariableBuildTool(minfo, "AS_FOR_BUILD", "as"); - SetNeedVariableBuildTool(minfo, "CC_FOR_BUILD", "gcc"); - SetNeedVariableBuildTool(minfo, "CPP_FOR_BUILD", "gcc -E"); - SetNeedVariableBuildTool(minfo, "CXXFILT_FOR_BUILD", "c++filt"); - SetNeedVariableBuildTool(minfo, "CXX_FOR_BUILD", "g++"); - SetNeedVariableBuildTool(minfo, "LD_FOR_BUILD", "ld"); - SetNeedVariableBuildTool(minfo, "NM_FOR_BUILD", "nm"); - SetNeedVariableBuildTool(minfo, "OBJCOPY_FOR_BUILD", "objcopy"); - SetNeedVariableBuildTool(minfo, "OBJDUMP_FOR_BUILD", "objdump"); - SetNeedVariableBuildTool(minfo, "RANLIB_FOR_BUILD", "ranlib"); - SetNeedVariableBuildTool(minfo, "READELF_FOR_BUILD", "readelf"); - SetNeedVariableBuildTool(minfo, "STRIP_FOR_BUILD", "strip"); - - SetNeedVariableCrossTool(minfo, "AR", "ar"); - SetNeedVariableCrossTool(minfo, "AS", "as"); - SetNeedVariableCrossTool(minfo, "CC", "gcc"); - SetNeedVariableCrossTool(minfo, "CPP", "gcc -E"); - SetNeedVariableCrossTool(minfo, "CXXFILT", "c++filt"); - SetNeedVariableCrossTool(minfo, "CXX", "g++"); - SetNeedVariableCrossTool(minfo, "LD", "ld"); - SetNeedVariableCrossTool(minfo, "NM", "nm"); - SetNeedVariableCrossTool(minfo, "OBJCOPY", "objcopy"); - SetNeedVariableCrossTool(minfo, "OBJDUMP", "objdump"); - SetNeedVariableCrossTool(minfo, "RANLIB", "ranlib"); - SetNeedVariableCrossTool(minfo, "READELF", "readelf"); - SetNeedVariableCrossTool(minfo, "STRIP", "strip"); -} - -void Configure(metainfo_t* minfo, const char* subdir) -{ - if ( fork_and_wait_or_recovery() ) + if ( minfo->bootstrapping ) { - string_array_t* pkg_info = &minfo->package_info; - const char* configure_raw = - dictionary_get_def(pkg_info, "pkg.configure.cmd", "./configure"); - char* configure; - if ( strcmp(minfo->build_dir, minfo->package_dir) == 0 ) - configure = strdup(configure_raw); - else - configure = print_string("%s/%s", minfo->package_dir, configure_raw); - const char* conf_extra_args = - dictionary_get_def(pkg_info, "pkg.configure.args", ""); - const char* conf_extra_vars = - dictionary_get_def(pkg_info, "pkg.configure.vars", ""); - bool with_sysroot = - parse_boolean(dictionary_get_def(pkg_info, "pkg.configure.with-sysroot", - "false")); - // TODO: I am unclear if this issue still affects gcc, I might have - // forgotten to set pkg.configure.with-sysroot-ld-bug=true there. - const char* with_sysroot_ld_bug_default = "false"; - if ( !strcmp(minfo->package_name, "gcc") ) - with_sysroot_ld_bug_default = "true"; - bool with_sysroot_ld_bug = - parse_boolean(dictionary_get_def(pkg_info, "pkg.configure.with-sysroot-ld-bug", - with_sysroot_ld_bug_default )); - bool with_build_sysroot = - parse_boolean(dictionary_get_def(pkg_info, "pkg.configure.with-build-sysroot", - "false")); - if ( chdir(minfo->build_dir) != 0 ) - err(1, "chdir: `%s'", minfo->build_dir); - if ( subdir && chdir(subdir) != 0 ) - err(1, "chdir: `%s/%s'", minfo->build_dir, subdir); - SetNeededVariables(minfo); - string_array_t env_vars = string_array_make(); - string_array_append_token_string(&env_vars, conf_extra_vars); - for ( size_t i = 0; i < env_vars.length; i++ ) + for ( size_t i = 0; i < BUILDVARS_LENGTH; i++ ) + unsetenv(buildvars[i].variable); + for ( size_t i = 0; i < BUILDVARS_LENGTH; i++ ) { - char* key = env_vars.strings[i]; - assert(key); - char* assignment = strchr((char*) key, '='); - if ( !assignment ) - { - if ( !strncmp(key, "unset ", strlen("unset ")) ) - unsetenv(key + strlen("unset ")); - continue; - } - *assignment = '\0'; - char* value = assignment+1; - setenv(key, value, 1); + char* for_build = + print_string("%s_FOR_BUILD", buildvars[i].variable); + if ( !for_build ) + err(1, "malloc"); + const char* value = getenv(for_build); + if ( value && setenv(buildvars[i].variable, value, 1) < 0 ) + err(1, "setenv"); + free(for_build); } - const char* fixed_cmd_argv[] = - { - configure, - print_string("--build=%s", minfo->build), - print_string("--host=%s", minfo->host), - print_string("--target=%s", minfo->target), - print_string("--prefix=%s", minfo->prefix), - print_string("--exec-prefix=%s", minfo->exec_prefix), - NULL - }; - string_array_t args = string_array_make(); - for ( size_t i = 0; fixed_cmd_argv[i]; i++ ) - string_array_append(&args, fixed_cmd_argv[i]); - if ( minfo->sysroot && with_build_sysroot ) - { - string_array_append(&args, print_string("--with-build-sysroot=%s", - minfo->sysroot)); - if ( minfo->sysroot && with_sysroot ) - { - // TODO: Binutils has a bug where the empty string means that - // sysroot support is disabled and ld --sysroot won't work - // so set it to / here for compatibility. - // TODO: GCC has a bug where it doesn't use the - // --with-build-sysroot value when --with-sysroot= when - // locating standard library headers. - if ( with_sysroot_ld_bug ) - string_array_append(&args, "--with-sysroot=/"); - else - string_array_append(&args, "--with-sysroot="); - } - } - else if ( minfo->sysroot && with_sysroot ) - { - string_array_append(&args, print_string("--with-sysroot=%s", - minfo->sysroot)); - } - string_array_append_token_string(&args, conf_extra_args); - string_array_append(&args, NULL); - recovery_execvp(args.strings[0], (char* const*) args.strings); - err(127, "`%s'", args.strings[0]); + return; } + + for ( size_t i = 0; i < BUILDVARS_LENGTH; i++ ) + { + if ( !buildvars[i].program && !getenv(buildvars[i].variable) ) + continue; + char* for_build = print_string("%s_FOR_BUILD", buildvars[i].variable); + if ( !for_build ) + err(1, "malloc"); + SetNeedVariableBuildTool(minfo, for_build, buildvars[i].value); + free(for_build); + } + for ( size_t i = 0; i < BUILDVARS_LENGTH; i++ ) + if ( buildvars[i].program ) + SetNeedVariableCrossTool(minfo, buildvars[i].variable, + buildvars[i].value); } -bool TestDirty(metainfo_t* minfo, - const char* subdir, - const char* candidate) +static void Configure(struct metainfo* minfo) +{ + if ( !fork_and_wait_or_recovery() ) + return; + string_array_t* pkg_info = &minfo->package_info; + const char* subdir = dictionary_get(pkg_info, "pkg.subdir"); + const char* configure_raw = + dictionary_get_def(pkg_info, "pkg.configure.cmd", "./configure"); + char* configure; + if ( strcmp(minfo->build_dir, minfo->package_dir) == 0 ) + configure = strdup(configure_raw); + else + configure = join_paths(minfo->package_dir, configure_raw); + if ( !configure ) + err(1, "malloc"); + const char* conf_extra_args = + dictionary_get_def(pkg_info, "pkg.configure.args", ""); + const char* conf_extra_vars = + dictionary_get_def(pkg_info, "pkg.configure.vars", ""); + bool with_sysroot = + parse_boolean(dictionary_get_def(pkg_info, + "pkg.configure.with-sysroot", "false")); + // TODO: I am unclear if this issue still affects gcc, I might have + // forgotten to set pkg.configure.with-sysroot-ld-bug=true there. + const char* with_sysroot_ld_bug_default = "false"; + if ( !strcmp(minfo->package_name, "gcc") ) + with_sysroot_ld_bug_default = "true"; + bool with_sysroot_ld_bug = + parse_boolean(dictionary_get_def(pkg_info, + "pkg.configure.with-sysroot-ld-bug", with_sysroot_ld_bug_default )); + bool with_build_sysroot = + parse_boolean(dictionary_get_def(pkg_info, + "pkg.configure.with-build-sysroot", "false")); + if ( chdir(minfo->build_dir) != 0 ) + err(1, "chdir: `%s'", minfo->build_dir); + if ( subdir && chdir(subdir) != 0 ) + err(1, "chdir: `%s/%s'", minfo->build_dir, subdir); + SetNeededVariables(minfo); + string_array_t env_vars = string_array_make(); + string_array_append_token_string(&env_vars, conf_extra_vars); + for ( size_t i = 0; i < env_vars.length; i++ ) + { + char* key = env_vars.strings[i]; + assert(key); + char* assignment = strchr((char*) key, '='); + if ( !assignment ) + { + if ( !strncmp(key, "unset ", strlen("unset ")) ) + unsetenv(key + strlen("unset ")); + continue; + } + *assignment = '\0'; + char* value = assignment+1; + setenv(key, value, 1); + } + const char* fixed_cmd_argv[] = + { + configure, + print_string("--prefix=%s", minfo->prefix), + print_string("--exec-prefix=%s", minfo->exec_prefix), + print_string("--build=%s", minfo->build), + minfo->bootstrapping ? NULL : + print_string("--host=%s", minfo->host), + print_string("--target=%s", minfo->target), + NULL + }; + string_array_t args = string_array_make(); + for ( size_t i = 0; fixed_cmd_argv[i]; i++ ) + string_array_append(&args, fixed_cmd_argv[i]); + if ( minfo->sysroot && with_build_sysroot ) + { + string_array_append(&args, print_string("--with-build-sysroot=%s", + minfo->sysroot)); + if ( minfo->sysroot && with_sysroot ) + { + // TODO: Binutils has a bug where the empty string means that + // sysroot support is disabled and ld --sysroot won't work + // so set it to / here for compatibility. + // TODO: GCC has a bug where it doesn't use the + // --with-build-sysroot value when --with-sysroot= when + // locating standard library headers. + if ( with_sysroot_ld_bug ) + string_array_append(&args, "--with-sysroot=/"); + else + string_array_append(&args, "--with-sysroot="); + } + } + else if ( minfo->sysroot && with_sysroot ) + { + string_array_append(&args, print_string("--with-sysroot=%s", + minfo->sysroot)); + } + string_array_append_token_string(&args, conf_extra_args); + string_array_append(&args, NULL); + recovery_execvp(args.strings[0], (char* const*) args.strings); + err(127, "`%s'", args.strings[0]); +} + +static bool TestDirty(struct metainfo* minfo, + const char* subdir, + const char* candidate) { if ( !subdir ) subdir = "."; @@ -498,10 +527,11 @@ bool TestDirty(metainfo_t* minfo, return result; } -bool IsDirty(metainfo_t* minfo, const char* subdir) +static bool IsDirty(struct metainfo* minfo) { string_array_t* pkg_info = &minfo->package_info; const char* dirty_file = dictionary_get(pkg_info, "pkg.dirty-file"); + const char* subdir = dictionary_get(pkg_info, "pkg.subdir"); if ( dirty_file ) return TestDirty(minfo, subdir, dirty_file); return TestDirty(minfo, subdir, "config.log") || @@ -509,87 +539,282 @@ bool IsDirty(metainfo_t* minfo, const char* subdir) TestDirty(minfo, subdir, "makefile"); } -void Make(metainfo_t* minfo, const char* make_target, const char* destdir, - bool die_on_error, const char* subdir) +static void Make(struct metainfo* minfo, + const char* make_target, + const char* destdir, + bool die_on_error, + const char* subdir) { - if ( (!die_on_error && fork_and_wait_or_death_def(die_on_error)) || - (die_on_error && fork_and_wait_or_recovery()) ) + if ( !(die_on_error ? + fork_and_wait_or_recovery() : + fork_and_wait_or_death_def(false)) ) + return; + + string_array_t* pkg_info = &minfo->package_info; + char* make = strdup(minfo->make); + const char* override_make = dictionary_get(pkg_info, "pkg.make.cmd"); + const char* make_extra_args = + dictionary_get_def(pkg_info, "pkg.make.args", ""); + const char* make_extra_vars = + dictionary_get_def(pkg_info, "pkg.make.vars", ""); + if ( override_make ) { - string_array_t* pkg_info = &minfo->package_info; - char* make = strdup(minfo->make); - const char* override_make = dictionary_get(pkg_info, "pkg.make.cmd"); - const char* make_extra_args = dictionary_get_def(pkg_info, "pkg.make.args", ""); - const char* make_extra_vars = dictionary_get_def(pkg_info, "pkg.make.vars", ""); - if ( override_make ) - { - free(make); - make = join_paths(minfo->package_dir, override_make); - } - SetNeededVariables(minfo); - if ( chdir(minfo->build_dir) != 0 ) - err(1, "chdir: `%s'", minfo->build_dir); - if ( subdir && chdir(subdir) != 0 ) - err(1, "chdir: `%s/%s'", minfo->build_dir, subdir); - if ( destdir ) - setenv("DESTDIR", destdir, 1); - setenv("BUILD", minfo->build, 1); - setenv("HOST", minfo->host, 1); - setenv("TARGET", minfo->target, 1); - if ( minfo->prefix ) - setenv("PREFIX", minfo->prefix, 1); - else - unsetenv("PREFIX"); - if ( minfo->exec_prefix ) - setenv("EXEC_PREFIX", minfo->exec_prefix, 1); - else - unsetenv("EXEC_PREFIX"); - if ( minfo->makeflags ) - setenv("MAKEFLAGS", minfo->makeflags, 1); - setenv("MAKE", minfo->make, 1); - string_array_t env_vars = string_array_make(); - string_array_append_token_string(&env_vars, make_extra_vars); - for ( size_t i = 0; i < env_vars.length; i++ ) - { - char* key = env_vars.strings[i]; - assert(key); - char* assignment = strchr((char*) key, '='); - if ( !assignment ) - { - if ( !strncmp(key, "unset ", strlen("unset ")) ) - unsetenv(key + strlen("unset ")); - continue; - } - *assignment = '\0'; - char* value = assignment+1; - setenv(key, value, 1); - } - const char* fixed_cmd_argv[] = - { - make, - NULL - }; - string_array_t args = string_array_make(); - for ( size_t i = 0; fixed_cmd_argv[i]; i++ ) - string_array_append(&args, fixed_cmd_argv[i]); - string_array_append_token_string(&args, make_target); - string_array_append_token_string(&args, make_extra_args); - if ( !die_on_error ) - string_array_append(&args, "-k"); - string_array_append(&args, NULL); - if ( die_on_error ) - recovery_execvp(args.strings[0], (char* const*) args.strings); - else - execvp(args.strings[0], (char* const*) args.strings); - err(127, "`%s'", args.strings[0]); + free(make); + make = join_paths(minfo->package_dir, override_make); } + SetNeededVariables(minfo); + if ( chdir(minfo->build_dir) != 0 ) + err(1, "chdir: `%s'", minfo->build_dir); + if ( subdir && chdir(subdir) != 0 ) + err(1, "chdir: `%s/%s'", minfo->build_dir, subdir); + if ( !minfo->bootstrapping && destdir ) + setenv("DESTDIR", destdir, 1); + setenv("BUILD", minfo->build, 1); + setenv("HOST", minfo->host, 1); + setenv("TARGET", minfo->target, 1); + if ( minfo->prefix ) + setenv("PREFIX", minfo->prefix, 1); + else + unsetenv("PREFIX"); + if ( minfo->exec_prefix ) + setenv("EXEC_PREFIX", minfo->exec_prefix, 1); + else + unsetenv("EXEC_PREFIX"); + if ( minfo->makeflags ) + setenv("MAKEFLAGS", minfo->makeflags, 1); + setenv("MAKE", minfo->make, 1); + string_array_t env_vars = string_array_make(); + string_array_append_token_string(&env_vars, make_extra_vars); + for ( size_t i = 0; i < env_vars.length; i++ ) + { + char* key = env_vars.strings[i]; + assert(key); + char* assignment = strchr((char*) key, '='); + if ( !assignment ) + { + if ( !strncmp(key, "unset ", strlen("unset ")) ) + unsetenv(key + strlen("unset ")); + continue; + } + *assignment = '\0'; + char* value = assignment+1; + setenv(key, value, 1); + } + const char* fixed_cmd_argv[] = { make, NULL }; + string_array_t args = string_array_make(); + for ( size_t i = 0; fixed_cmd_argv[i]; i++ ) + string_array_append(&args, fixed_cmd_argv[i]); + string_array_append_token_string(&args, make_target); + string_array_append_token_string(&args, make_extra_args); + if ( !die_on_error ) + string_array_append(&args, "-k"); + string_array_append(&args, NULL); + if ( die_on_error ) + recovery_execvp(args.strings[0], (char* const*) args.strings); + else + execvp(args.strings[0], (char* const*) args.strings); + err(127, "`%s'", args.strings[0]); } -void BuildPackage(metainfo_t* minfo) +static void Clean(struct metainfo* minfo) { - // Detect which build system we are interfacing with. string_array_t* pinfo = &minfo->package_info; + const char* subdir = dictionary_get(pinfo, "pkg.subdir"); + const char* build_system = + dictionary_get_def(pinfo, "pkg.build-system", "none"); + const char* default_clean_target = + !strcmp(build_system, "configure") ? "distclean" : "clean"; + const char* clean_target = + dictionary_get_def(pinfo, "pkg.make.clean-target", + default_clean_target); + const char* ignore_clean_failure_var = + dictionary_get_def(pinfo, "pkg.make.ignore-clean-failure", "true"); + bool ignore_clean_failure = parse_boolean(ignore_clean_failure_var); + + Make(minfo, clean_target, NULL, !ignore_clean_failure, subdir); +} + +static void Build(struct metainfo* minfo) +{ + string_array_t* pinfo = &minfo->package_info; + const char* subdir = dictionary_get(pinfo, "pkg.subdir"); + const char* build_target = + dictionary_get_def(pinfo, "pkg.make.build-target", "all"); + + Make(minfo, build_target, NULL, true, subdir); +} + +static void CreateDestination(void) +{ + char* tardir_rel = join_paths(tmp_root, "tix"); + char* destdir_rel = join_paths(tardir_rel, "data"); + char* tixdir_rel = join_paths(tardir_rel, "tix"); + + if ( mkdir(tardir_rel, 0777) < 0 ) + err(1, "mkdir: %s", tardir_rel); + if ( mkdir(destdir_rel, 0755) != 0 ) + err(1, "mkdir: `%s'", destdir_rel); + if ( mkdir(tixdir_rel, 0755) != 0 ) + err(1, "mkdir: `%s'", tixdir_rel); + + free(tardir_rel); + free(destdir_rel); + free(tixdir_rel); +} + +static void Install(struct metainfo* minfo) +{ + string_array_t* pinfo = &minfo->package_info; + const char* subdir = dictionary_get(pinfo, "pkg.subdir"); + const char* install_target = + dictionary_get_def(pinfo, "pkg.make.install-target", "install"); + char* tardir_rel = join_paths(tmp_root, "tix"); + char* destdir_rel = join_paths(tardir_rel, "data"); + char* destdir = realpath(destdir_rel, NULL); + if ( !destdir ) + err(1, "realpath: %s", destdir_rel); + + Make(minfo, install_target, destdir, true, subdir); + + free(tardir_rel); + free(destdir_rel); + free(destdir); +} + +static void PostInstall(struct metainfo* minfo) +{ + string_array_t* pinfo = &minfo->package_info; + const char* post_install_cmd = + dictionary_get(pinfo, "pkg.post-install.cmd"); + if ( !post_install_cmd ) + return; + + if ( !fork_and_wait_or_recovery() ) + return; + + const char* subdir = dictionary_get(pinfo, "pkg.subdir"); + char* tardir_rel = join_paths(tmp_root, "tix"); + char* destdir_rel = join_paths(tardir_rel, "data"); + char* destdir = realpath(destdir_rel, NULL); + if ( !destdir ) + err(1, "realpath: %s", destdir_rel); + + SetNeededVariables(minfo); + if ( chdir(minfo->package_dir) != 0 ) + err(1, "chdir: `%s'", minfo->package_dir); + if ( subdir && chdir(subdir) != 0 ) + err(1, "chdir: `%s/%s'", minfo->build_dir, subdir); + setenv("TIX_BUILD_DIR", minfo->build_dir, 1); + setenv("TIX_SOURCE_DIR", minfo->package_dir, 1); + setenv("TIX_INSTALL_DIR", destdir, 1); + if ( minfo->sysroot ) + setenv("TIX_SYSROOT", minfo->sysroot, 1); + else + unsetenv("TIX_SYSROOT"); + setenv("BUILD", minfo->build, 1); + setenv("HOST", minfo->host, 1); + setenv("TARGET", minfo->target, 1); + if ( minfo->prefix ) + setenv("PREFIX", minfo->prefix, 1); + else + unsetenv("PREFIX"); + if ( minfo->exec_prefix ) + setenv("EXEC_PREFIX", minfo->exec_prefix, 1); + else + unsetenv("EXEC_PREFIX"); + const char* cmd_argv[] = + { + post_install_cmd, + NULL + }; + recovery_execvp(cmd_argv[0], (char* const*) cmd_argv); + err(127, "%s", cmd_argv[0]); +} + +static void TixInfo(struct metainfo* minfo) +{ + string_array_t* pinfo = &minfo->package_info; + char* tardir_rel = join_paths(tmp_root, "tix"); + if ( !tardir_rel ) + err(1, "malloc"); + char* tixinfo_rel = join_paths(tardir_rel, "tix/tixinfo"); + if ( !tixinfo_rel ) + err(1, "malloc"); + const char* alias = dictionary_get(pinfo, "pkg.alias-of"); + const char* runtime_deps = dictionary_get(pinfo, "pkg.runtime-deps"); + bool location_independent = + parse_boolean(dictionary_get_def(pinfo, + "pkg.location-independent", "false")); + + FILE* tixinfo_fp = fopen(tixinfo_rel, "w"); + if ( !tixinfo_fp ) + err(1, "`%s'", tixinfo_rel); + + fprintf(tixinfo_fp, "tix.version=1\n"); + fprintf(tixinfo_fp, "tix.class=tix\n"); + fprintf(tixinfo_fp, "tix.platform=%s\n", minfo->host); + fprintf(tixinfo_fp, "pkg.name=%s\n", minfo->package_name); + if ( alias ) + fprintf(tixinfo_fp, "pkg.alias-of=%s\n", alias); + else + { + if ( runtime_deps ) + fprintf(tixinfo_fp, "pkg.runtime-deps=%s\n", runtime_deps); + if ( location_independent ) + fprintf(tixinfo_fp, "pkg.location-independent=true\n"); + else + fprintf(tixinfo_fp, "pkg.prefix=%s\n", minfo->prefix); + } + + if ( ferror(tixinfo_fp) || fflush(tixinfo_fp) == EOF ) + err(1, "write: `%s'", tixinfo_rel); + + fclose(tixinfo_fp); + free(tardir_rel); + free(tixinfo_rel); +} + +static void Package(struct metainfo* minfo) +{ + if ( !fork_and_wait_or_recovery() ) + return; + + char* tardir_rel = join_paths(tmp_root, "tix"); + if ( !tardir_rel ) + err(1, "malloc"); + char* package_tix = print_string("%s/%s.tix.tar.xz", + minfo->destination, minfo->package_name); + if ( !package_tix ) + err(1, "malloc"); + printf("Creating `%s'...\n", package_tix); + + const char* cmd_argv[] = + { + minfo->tar, + "-C", tardir_rel, + "--remove-files", + "--create", + "--xz", + "--numeric-owner", + "--owner=0", + "--group=0", + "--file", package_tix, + "tix", + "data", + NULL + }; + recovery_execvp(cmd_argv[0], (char* const*) cmd_argv); + err(127, "%s", cmd_argv[0]); +} + +static void Compile(struct metainfo* minfo) +{ + string_array_t* pinfo = &minfo->package_info; + + // Detect which build system we are interfacing with. const char* build_system = dictionary_get(pinfo, "pkg.build-system"); - assert(build_system); + if ( !build_system ) + errx(1, "%s: pkg.build-system was not found", minfo->package_info_path); // Determine whether need to do an out-of-directory build. const char* use_build_dir_var = @@ -597,7 +822,11 @@ void BuildPackage(metainfo_t* minfo) bool use_build_dir = parse_boolean(use_build_dir_var); if ( use_build_dir ) { - minfo->build_dir = print_string("%s/build", tmp_root); + const char* build_rel = + minfo->bootstrapping ? "build-bootstrap" : "build"; + minfo->build_dir = join_paths(tmp_root, build_rel); + if ( !minfo->build_dir ) + err(1, "malloc"); if ( mkdir(minfo->build_dir, 0777) < 0 ) err(1, "mkdir %s", minfo->build_dir); } @@ -605,164 +834,142 @@ void BuildPackage(metainfo_t* minfo) minfo->build_dir = strdup(minfo->package_dir); // Reset the build directory if needed. - const char* default_clean_target = - !strcmp(build_system, "configure") ? "distclean" : "clean"; - const char* clean_target = dictionary_get_def(pinfo, "pkg.make.clean-target", - default_clean_target); - const char* ignore_clean_failure_var = - dictionary_get_def(pinfo, "pkg.make.ignore-clean-failure", "true"); - bool ignore_clean_failure = parse_boolean(ignore_clean_failure_var); - - const char* subdir = dictionary_get(pinfo, "pkg.subdir"); - if ( SHOULD_DO_BUILD_STEP(BUILD_STEP_PRE_CLEAN, minfo) && !use_build_dir && - IsDirty(minfo, NULL) ) - Make(minfo, clean_target, NULL, !ignore_clean_failure, NULL); + IsDirty(minfo) ) + Clean(minfo); // Configure the build directory if needed. if ( strcmp(build_system, "configure") == 0 && SHOULD_DO_BUILD_STEP(BUILD_STEP_CONFIGURE, minfo) ) - Configure(minfo, subdir); + Configure(minfo); + if ( SHOULD_DO_BUILD_STEP(BUILD_STEP_BUILD, minfo) ) + Build(minfo); + + if ( SHOULD_DO_BUILD_STEP(BUILD_STEP_INSTALL, minfo) ) + Install(minfo); + + if ( SHOULD_DO_BUILD_STEP(BUILD_STEP_POST_INSTALL, minfo) ) + PostInstall(minfo); + + // Clean the build directory after the successful build. + if ( SHOULD_DO_BUILD_STEP(BUILD_STEP_POST_CLEAN, minfo) ) + Clean(minfo); +} + +static void Bootstrap(struct metainfo* minfo) +{ + struct metainfo newinfo = { 0 }; + + newinfo.build = minfo->build; + newinfo.build_dir = NULL; + newinfo.destination = NULL; + newinfo.generation = minfo->generation; + newinfo.host = minfo->build; + newinfo.make = minfo->make; + newinfo.makeflags = minfo->makeflags; + newinfo.package_dir = minfo->package_dir; + newinfo.package_info_path = minfo->package_info_path; + newinfo.package_name = minfo->package_name; + newinfo.prefix = join_paths(tmp_root, "bootstrap"); + if ( !newinfo.prefix ) + err(1, "malloc"); + if ( mkdir(newinfo.prefix, 0777) < 0 ) + err(1, "mkdir: %s", newinfo.prefix); + newinfo.exec_prefix = newinfo.prefix; + newinfo.sysroot = NULL; + newinfo.tar = minfo->tar; + newinfo.target = minfo->host; + newinfo.tmp = minfo->tmp; + for ( size_t i = 0; i < minfo->package_info.length; i++ ) + { + const char* string = minfo->package_info.strings[i]; + if ( !strncmp(string, "pkg.", strlen("pkg.")) ) + continue; + if ( !strncmp(string, "bootstrap.", strlen("bootstrap.")) ) + { + const char* rest = string + strlen("bootstrap."); + char* newstring = print_string("pkg.%s", rest); + if ( !newstring ) + err(1, "malloc"); + if ( !string_array_append(&newinfo.package_info, newstring) ) + err(1, "malloc"); + free(newstring); + } + else + { + if ( !string_array_append(&newinfo.package_info, string) ) + err(1, "malloc"); + } + } + newinfo.start_step = BUILD_STEP_PRE_CLEAN; + newinfo.end_step = BUILD_STEP_POST_CLEAN; + newinfo.bootstrapping = true; + newinfo.cross = false; + + Compile(&newinfo); + + char* bindir = join_paths(newinfo.prefix, "bin"); + if ( !bindir ) + err(1, "malloc"); + if ( access(bindir, F_OK) == 0 ) + append_to_path(bindir); + free(bindir); + + char* sbindir = join_paths(newinfo.prefix, "sbin"); + if ( !sbindir ) + err(1, "malloc"); + if ( access(sbindir, F_OK) == 0 ) + append_to_path(sbindir); + free(sbindir); + + string_array_reset(&newinfo.package_info); + free(newinfo.prefix); +} + +static void BuildPackage(struct metainfo* minfo) +{ + string_array_t* pinfo = &minfo->package_info; + + // Whether this is just an alias for another package. + const char* alias = dictionary_get(pinfo, "pkg.alias-of"); + + // Determine if the package is location independent. bool location_independent = - parse_boolean(dictionary_get_def(pinfo, "pkg.location-independent", "false")); - - const char* build_target = dictionary_get_def(pinfo, "pkg.make.build-target", "all"); - const char* install_target = dictionary_get_def(pinfo, "pkg.make.install-target", "install"); - - if ( !location_independent && !minfo->prefix ) + parse_boolean(dictionary_get_def(pinfo, "pkg.location-independent", + "false")); + if ( !alias && !location_independent && !minfo->prefix ) errx(1, "error: %s is not location independent and you need to " "specify the intended destination prefix using --prefix", minfo->package_name); - if ( SHOULD_DO_BUILD_STEP(BUILD_STEP_BUILD, minfo) ) - Make(minfo, build_target, NULL, true, subdir); + CreateDestination(); - char* tardir_rel = print_string("%s/%s", tmp_root, "tix"); - if ( mkdir(tardir_rel, 0777) < 0 ) - err(1, "mkdir: %s", tardir_rel); + // Possibly build a native version of the package to aid cross-compilation. + // This is an anti-feature needed for broken packages that don't properly + // handle this case entirely themselves. There's a few packages that need + // the exact same version around natively in order to cross-compile. + const char* use_bootstrap_var = + dictionary_get_def(pinfo, "pkg.use-bootstrap", "false"); + bool use_bootstrap = parse_boolean(use_bootstrap_var); + if ( !alias && use_bootstrap && strcmp(minfo->build, minfo->host) != 0 && + SHOULD_DO_BUILD_STEP(BUILD_STEP_CONFIGURE, minfo) ) + Bootstrap(minfo); - char* destdir_rel = print_string("%s/%s", tardir_rel, "data"); - char* tixdir_rel = print_string("%s/%s", tardir_rel, "tix"); - char* tixinfo_rel = print_string("%s/%s", tardir_rel, "tix/tixinfo"); + EmitWrappers(minfo); - if ( mkdir(destdir_rel, 0755) != 0 ) - err(1, "mkdir: `%s'", destdir_rel); - if ( mkdir(tixdir_rel, 0755) != 0 ) - err(1, "mkdir: `%s'", tixdir_rel); - - char* destdir = realpath(destdir_rel, NULL); - if ( !destdir ) - err(1, "realpath: %s", destdir_rel); - - if ( SHOULD_DO_BUILD_STEP(BUILD_STEP_INSTALL, minfo) ) - Make(minfo, install_target, destdir, true, subdir); - - const char* post_install_cmd = dictionary_get(pinfo, "pkg.post-install.cmd"); - - if ( post_install_cmd && - SHOULD_DO_BUILD_STEP(BUILD_STEP_POST_INSTALL, minfo) && - fork_and_wait_or_recovery() ) - { - if ( chdir(minfo->package_dir) != 0 ) - err(1, "chdir: `%s'", minfo->package_dir); - setenv("TIX_BUILD_DIR", minfo->build_dir, 1); - setenv("TIX_SOURCE_DIR", minfo->package_dir, 1); - setenv("TIX_INSTALL_DIR", destdir, 1); - if ( minfo->sysroot ) - setenv("TIX_SYSROOT", minfo->sysroot, 1); - else - unsetenv("TIX_SYSROOT"); - setenv("BUILD", minfo->build, 1); - setenv("HOST", minfo->host, 1); - setenv("TARGET", minfo->target, 1); - if ( minfo->prefix ) - setenv("PREFIX", minfo->prefix, 1); - else - unsetenv("PREFIX"); - if ( minfo->exec_prefix ) - setenv("EXEC_PREFIX", minfo->exec_prefix, 1); - else - unsetenv("EXEC_PREFIX"); - const char* cmd_argv[] = - { - post_install_cmd, - NULL - }; - recovery_execvp(cmd_argv[0], (char* const*) cmd_argv); - err(127, "%s", cmd_argv[0]); - } - - const char* tix_ext = ".tix.tar.xz"; - char* package_tix = print_string("%s/%s%s", minfo->destination, - minfo->package_name, tix_ext); - - FILE* tixinfo_fp = fopen(tixinfo_rel, "w"); - if ( !tixinfo_fp ) - err(1, "`%s'", tixinfo_rel); - - const char* runtime_deps = dictionary_get(pinfo, "pkg.runtime-deps"); - - fprintf(tixinfo_fp, "tix.version=1\n"); - fprintf(tixinfo_fp, "tix.class=tix\n"); - fprintf(tixinfo_fp, "tix.platform=%s\n", minfo->host); - fprintf(tixinfo_fp, "pkg.name=%s\n", minfo->package_name); - if ( runtime_deps ) - fprintf(tixinfo_fp, "pkg.runtime-deps=%s\n", runtime_deps); - if ( location_independent ) - fprintf(tixinfo_fp, "pkg.location-independent=true\n"); - else - fprintf(tixinfo_fp, "pkg.prefix=%s\n", minfo->prefix); - - if ( ferror(tixinfo_fp) ) - err(1, "write: `%s'", tixinfo_rel); - - fclose(tixinfo_fp); + if ( !alias ) + Compile(minfo); if ( SHOULD_DO_BUILD_STEP(BUILD_STEP_PACKAGE, minfo) ) { - printf("Creating `%s'...\n", package_tix); - if ( fork_and_wait_or_recovery() ) - { - const char* cmd_argv[] = - { - minfo->tar, - "-C", tardir_rel, - "--remove-files", - "--create", - "--xz", - "--numeric-owner", - "--owner=0", - "--group=0", - "--file", package_tix, - "tix", - "data", - NULL - }; - recovery_execvp(cmd_argv[0], (char* const*) cmd_argv); - err(127, "%s", cmd_argv[0]); - } + TixInfo(minfo); + Package(minfo); } - - unlink(tixinfo_rel); - rmdir(destdir_rel); - rmdir(tixdir_rel); - //rmdir(tardir_rel); // Keep around to avoid on_exit handler race. - - free(tardir_rel); - free(destdir_rel); - free(tixdir_rel); - free(tixinfo_rel); - - free(package_tix); - - // Clean the build directory after the successful build. - if ( SHOULD_DO_BUILD_STEP(BUILD_STEP_POST_CLEAN, minfo) ) - Make(minfo, clean_target, NULL, !ignore_clean_failure, NULL); } -void VerifySourceTixInformation(metainfo_t* minfo) +static void VerifySourceTixInformation(struct metainfo* minfo) { const char* pipath = minfo->package_info_path; string_array_t* pinfo = &minfo->package_info; @@ -778,7 +985,8 @@ void VerifySourceTixInformation(metainfo_t* minfo) errx(1, "error: `%s': tix class `%s' is not `srctix': this object " "is not suitable for compilation.", pipath, tix_class); VerifyInfoVariable(pinfo, "pkg.name", pipath); - VerifyInfoVariable(pinfo, "pkg.build-system", pipath); + if ( !dictionary_get(pinfo, "pkg.alias-of") ) + VerifyInfoVariable(pinfo, "pkg.build-system", pipath); } // TODO: The MAKEFLAGS variable is actually not in the same format as the token @@ -786,7 +994,7 @@ void VerifySourceTixInformation(metainfo_t* minfo) // but instead consider them normal characters. This should work as // expected, though, as long as the MAKEFLAGS variable doesn't contain any // quote characters. -void PurifyMakeflags(void) +static void PurifyMakeflags(void) { const char* makeflags_environment = getenv("MAKEFLAGS"); if ( !makeflags_environment ) @@ -830,7 +1038,7 @@ int main(int argc, char* argv[]) { PurifyMakeflags(); - metainfo_t minfo; + struct metainfo minfo; memset(&minfo, 0, sizeof(minfo)); minfo.build = NULL; minfo.destination = strdup("."); @@ -846,6 +1054,7 @@ int main(int argc, char* argv[]) char* tmp = strdup(getenv_def("TMPDIR", "/tmp")); char* start_step_string = strdup("start"); char* end_step_string = strdup("end"); + char* source_package = NULL; const char* argv0 = argv[0]; for ( int i = 0; i < argc; i++ ) @@ -880,6 +1089,7 @@ int main(int argc, char* argv[]) else if ( GET_OPTION_VARIABLE("--make", &minfo.make) ) { } else if ( GET_OPTION_VARIABLE("--prefix", &minfo.prefix) ) { } else if ( GET_OPTION_VARIABLE("--exec-prefix", &minfo.exec_prefix) ) { } + else if ( GET_OPTION_VARIABLE("--source-package", &source_package) ) { } else if ( GET_OPTION_VARIABLE("--start", &start_step_string) ) { } else if ( GET_OPTION_VARIABLE("--sysroot", &minfo.sysroot) ) { } else if ( GET_OPTION_VARIABLE("--target", &minfo.target) ) { } @@ -933,9 +1143,10 @@ int main(int argc, char* argv[]) initialize_tmp(tmp, "tixbuild"); - minfo.package_dir = realpath(argv[1], NULL); + const char* srctix = argv[1]; + minfo.package_dir = realpath(srctix, NULL); if ( !minfo.package_dir ) - err(1, "realpath: %s", argv[1]); + err(1, "%s", srctix); if ( minfo.build && !minfo.build[0] ) free(minfo.build), minfo.build = NULL; @@ -951,14 +1162,17 @@ int main(int argc, char* argv[]) if ( !minfo.target ) minfo.target = strdup(minfo.host); + minfo.cross = strcmp(minfo.build, minfo.host) != 0 || minfo.sysroot; + if ( minfo.prefix && !minfo.exec_prefix ) minfo.exec_prefix = strdup(minfo.prefix); if ( !IsDirectory(minfo.package_dir) ) err(1, "`%s'", minfo.package_dir); - minfo.package_info_path = print_string("%s/tixbuildinfo", - minfo.package_dir); + minfo.package_info_path = join_paths(minfo.package_dir, "tixbuildinfo"); + if ( !minfo.package_info_path ) + err(1, "malloc"); minfo.package_info = string_array_make(); string_array_t* package_info = &minfo.package_info; @@ -972,7 +1186,23 @@ int main(int argc, char* argv[]) VerifySourceTixInformation(&minfo); minfo.package_name = strdup(dictionary_get(package_info, "pkg.name")); - emit_pkg_config_wrapper(&minfo); + const char* pkg_source_package = + dictionary_get(package_info, "pkg.source-package"); + if ( pkg_source_package && !source_package ) + { + source_package = print_string("%s/../%s", srctix, pkg_source_package); + if ( !source_package ) + err(1, "malloc"); + } + + if ( source_package ) + { + free(minfo.package_dir); + minfo.package_dir = realpath(source_package, NULL); + if ( !minfo.package_dir ) + err(1, "%s: looking for source package: %s", + srctix, source_package); + } BuildPackage(&minfo); diff --git a/tix/tix.c b/tix/tix.c index 1fce591a..bed658ad 100644 --- a/tix/tix.c +++ b/tix/tix.c @@ -105,6 +105,8 @@ string_array_t GetPackageDependencies(params_t* params, const char* pkg_name) const char* deps = dictionary_get_def(&tixinfo, "pkg.runtime-deps", ""); string_array_append_token_string(&ret, deps); + const char* alias = dictionary_get_def(&tixinfo, "pkg.alias-of", ""); + string_array_append_token_string(&ret, alias); string_array_reset(&tixinfo);