Add the Tix package management system.
This commit is contained in:
parent
dce618af93
commit
b0d07b9142
4
Makefile
4
Makefile
|
@ -34,11 +34,13 @@ all: sysroot
|
|||
build-tools:
|
||||
$(MAKE) -C mkinitrd
|
||||
$(MAKE) -C mxmpp
|
||||
$(MAKE) -C tix
|
||||
|
||||
.PHONY: install-build-tools
|
||||
install-build-tools:
|
||||
$(MAKE) -C mkinitrd install
|
||||
$(MAKE) -C mxmpp install
|
||||
$(MAKE) -C tix install
|
||||
|
||||
.PHONY: sysroot-fsh
|
||||
sysroot-fsh:
|
||||
|
@ -99,7 +101,7 @@ sysroot: sysroot-system sysroot-source sysroot-overlay sysroot-home-directory
|
|||
|
||||
.PHONY: clean-core
|
||||
clean-core:
|
||||
(for D in $(MODULES); do $(MAKE) clean $(SUBMAKE_OPTIONS) --directory $$D || exit $$?; done)
|
||||
(for D in $(MODULES) tix; do $(MAKE) clean $(SUBMAKE_OPTIONS) --directory $$D || exit $$?; done)
|
||||
|
||||
.PHONY: clean-builds
|
||||
clean-builds:
|
||||
|
|
7
README
7
README
|
@ -91,9 +91,10 @@ The build scripts might not contain a copyright license in which case they are
|
|||
covered by the standard license for the software component they relate to.
|
||||
|
||||
Unless the license header in the source code states otherwise, the Sortix
|
||||
kernel, the filesystem servers, the initrd tools, the utilities, the games, and
|
||||
the benchmark programs are licensed under the GNU General Public License, either
|
||||
version 3 or (at your option) any later version.
|
||||
kernel, the filesystem servers, the initrd tools, the utilities, the games, the
|
||||
benchmark programs, and the tix package management programs are licensed under
|
||||
the GNU General Public License, either version 3 or (at your option) any later
|
||||
version.
|
||||
|
||||
Unless the license header in the source code states otherwise, the libc library
|
||||
and the libdispd library are licensed under the GNU Lesser General Public
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
*.tix*
|
||||
porttix-create
|
||||
srctix-create
|
||||
tix
|
||||
tix-build
|
||||
tix-collection
|
||||
tix-execdiff
|
||||
tix-execpatch
|
||||
tix-install
|
||||
tix-object-insert
|
||||
tix-rmpatch
|
|
@ -0,0 +1,44 @@
|
|||
include ../compiler.mak
|
||||
include ../version.mak
|
||||
include ../dirs.mak
|
||||
|
||||
OPTLEVEL?=-g -O2
|
||||
CPPFLAGS?=
|
||||
CXXFLAGS?=$(OPTLEVEL)
|
||||
|
||||
CPPFLAGS:=$(CPPFLAGS)
|
||||
CXXFLAGS:=$(CXXFLAGS) -Wall -Wextra -fno-exceptions -fno-rtti
|
||||
|
||||
BINARIES:=\
|
||||
porttix-create \
|
||||
srctix-create \
|
||||
tix \
|
||||
tix-build \
|
||||
tix-collection \
|
||||
tix-execdiff \
|
||||
tix-execpatch \
|
||||
tix-install \
|
||||
tix-object-insert \
|
||||
tix-rmpatch \
|
||||
|
||||
PROGRAMS:=\
|
||||
$(BINARIES) \
|
||||
tix-eradicate-libtool-la \
|
||||
|
||||
LIBS:=-lnettle
|
||||
|
||||
all: $(PROGRAMS)
|
||||
|
||||
.PHONY: all install clean
|
||||
|
||||
%: %.cpp util.h
|
||||
$(CXX) -std=gnu++11 $(CPPFLAGS) $(CXXFLAGS) $< -o $@ $(LIBS)
|
||||
|
||||
$(DESTDIR)$(BINDIR):
|
||||
mkdir -p $@
|
||||
|
||||
install: all $(DESTDIR)$(BINDIR)
|
||||
install $(PROGRAMS) $(DESTDIR)$(BINDIR)
|
||||
|
||||
clean:
|
||||
rm -f $(BINARIES)
|
|
@ -0,0 +1,436 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2013.
|
||||
|
||||
This file is part of Tix.
|
||||
|
||||
Tix is free software: you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
Tix is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Tix. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
porttix-create.cpp
|
||||
Creates a port tix by generating patches using source code and tarballs.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#define __STDC_CONSTANT_MACROS
|
||||
#define __STDC_LIMIT_MACROS
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <error.h>
|
||||
#include <fcntl.h>
|
||||
#include <libgen.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
int redirect(const char* path, int flags, mode_t mode = 0)
|
||||
{
|
||||
int fd = open(path, flags, mode);
|
||||
if ( fd < 0 )
|
||||
return -1;
|
||||
dup2(fd, 1);
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void help(FILE* fp, const char* argv0)
|
||||
{
|
||||
fprintf(fp, "Usage: %s [OPTION]... --tarball=TARBALL --normalized=NORMALIZED SOURCE-TIX\n", argv0);
|
||||
fprintf(fp, "Creates a port tix by generating patches using source code and tarballs.\n");
|
||||
}
|
||||
|
||||
void version(FILE* fp, const char* argv0)
|
||||
{
|
||||
help(fp, argv0);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
char* cp = strdup(getenv_def("CP", "cp"));
|
||||
char* diff = strdup(getenv_def("DIFF", "diff"));
|
||||
char* input_normalized_path = NULL;
|
||||
char* input_tarball_path = NULL;
|
||||
char* output_directory = strdup(".");
|
||||
char* output = NULL;
|
||||
char* tar = strdup(getenv_def("TAR", "tar"));
|
||||
char* tix_execdiff = strdup(getenv_def("TIX_EXECDIFF", "tix-execdiff"));
|
||||
char* tmp = strdup(getenv_def("TMP", "/tmp"));
|
||||
|
||||
const char* argv0 = argv[0];
|
||||
for ( int i = 0; i < argc; i++ )
|
||||
{
|
||||
const char* arg = argv[i];
|
||||
if ( arg[0] != '-' )
|
||||
continue;
|
||||
argv[i] = NULL;
|
||||
if ( !strcmp(arg, "--") )
|
||||
break;
|
||||
if ( arg[1] != '-' )
|
||||
{
|
||||
while ( char c = *++arg ) switch ( c )
|
||||
{
|
||||
default:
|
||||
fprintf(stderr, "%s: unknown option -- `%c'\n", argv0, c);
|
||||
help(stderr, argv0);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
else if ( !strcmp(arg, "--help") ) { help(stdout, argv0); exit(0); }
|
||||
else if ( !strcmp(arg, "--version") ) { version(stdout, argv0); exit(0); }
|
||||
else if ( GET_OPTION_VARIABLE("--cp", &cp) ) { }
|
||||
else if ( GET_OPTION_VARIABLE("--diff", &diff) ) { }
|
||||
else if ( GET_OPTION_VARIABLE("--normalized", &input_normalized_path) ) { }
|
||||
else if ( GET_OPTION_VARIABLE("--output-directory", &output_directory) ) { }
|
||||
else if ( GET_OPTION_VARIABLE("--output", &output) ) { }
|
||||
else if ( GET_OPTION_VARIABLE("--tarball", &input_tarball_path) ) { }
|
||||
else if ( GET_OPTION_VARIABLE("--tar", &tar) ) { }
|
||||
else if ( GET_OPTION_VARIABLE("--tix-execdiff", &tix_execdiff) ) { }
|
||||
else if ( GET_OPTION_VARIABLE("--tmp", &tmp) ) { }
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "%s: unknown option: `%s'\n", argv0, arg);
|
||||
help(stderr, argv0);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if ( argc == 1 )
|
||||
{
|
||||
help(stdout, argv0);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
CompactArguments(&argc, &argv);
|
||||
|
||||
if ( argc <= 1 )
|
||||
{
|
||||
fprintf(stderr, "%s: no source tix specified\n", argv0);
|
||||
help(stderr, argv0);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if ( 3 <= argc )
|
||||
{
|
||||
fprintf(stderr, "%s: unexpected extra operand `%s'\n", argv0, argv[2]);
|
||||
help(stderr, argv0);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
const char* input_srctix_path = argv[1];
|
||||
|
||||
if ( !IsDirectory(input_srctix_path) )
|
||||
error(1, errno, "`%s'", input_srctix_path);
|
||||
|
||||
char* tixbuildinfo_path = print_string("%s/tixbuildinfo", input_srctix_path);
|
||||
|
||||
string_array_t package_info = string_array_make();
|
||||
if ( !dictionary_append_file_path(&package_info, tixbuildinfo_path) )
|
||||
{
|
||||
if ( errno == ENOENT )
|
||||
fprintf(stderr, "%s: `%s' doesn't appear to be a source tix:\n",
|
||||
argv0, input_srctix_path);
|
||||
error(1, errno, "`%s'", tixbuildinfo_path);
|
||||
}
|
||||
|
||||
const char* package_name = strdup(dictionary_get(&package_info, "pkg.name"));
|
||||
|
||||
if ( !output )
|
||||
output = print_string("%s/%s.porttix.tar.xz", output_directory, package_name);
|
||||
|
||||
char* tmp_root = print_string("%s/tmppid.%ju", tmp, (uintmax_t) getpid());
|
||||
if ( mkdir_p(tmp_root, 0777) != 0 )
|
||||
error(1, errno, "mkdir: `%s'", tmp_root);
|
||||
|
||||
on_exit(cleanup_file_or_directory, tmp_root);
|
||||
|
||||
const char* tarball_basename = non_modify_basename(input_tarball_path);
|
||||
|
||||
char* rel_srctix_path = print_string("%s.srctix", package_name);
|
||||
char* rel_normalized_path = print_string("%s.normalized", package_name);
|
||||
|
||||
char* porttix_path = print_string("%s/%s", tmp_root, package_name);
|
||||
if ( mkdir_p(porttix_path, 0777) != 0 )
|
||||
error(1, errno, "mkdir: `%s'", porttix_path);
|
||||
|
||||
char* srctix_path = print_string("%s/%s", tmp_root, rel_srctix_path);
|
||||
if ( mkdir_p(srctix_path, 0777) != 0 )
|
||||
error(1, errno, "mkdir: `%s'", srctix_path);
|
||||
|
||||
char* normalized_path = print_string("%s/%s", tmp_root, rel_normalized_path);
|
||||
if ( mkdir_p(normalized_path, 0777) != 0 )
|
||||
error(1, errno, "mkdir: `%s'", normalized_path);
|
||||
|
||||
// Create the porttixinfo file.
|
||||
char* porttixinfo_path = join_paths(porttix_path, "porttixinfo");
|
||||
FILE* porttixinfo_fp = fopen(porttixinfo_path, "w");
|
||||
if ( !porttixinfo_fp )
|
||||
error(1, errno, "`%s'", porttixinfo_path);
|
||||
fprintf(porttixinfo_fp, "package_name %s\n", package_name);
|
||||
|
||||
// Copy the input source tix to the temporary root.
|
||||
if ( fork_and_wait_or_death() )
|
||||
{
|
||||
const char* cmd_argv[] =
|
||||
{
|
||||
cp,
|
||||
"-HRT",
|
||||
"--preserve=timestamps,links",
|
||||
"--",
|
||||
input_srctix_path,
|
||||
srctix_path,
|
||||
NULL,
|
||||
};
|
||||
execvp(cmd_argv[0], (char* const*) cmd_argv);
|
||||
error(127, errno, "%s", cmd_argv[0]);
|
||||
}
|
||||
|
||||
// If no tarball exists, then package up the source directory!
|
||||
if ( !input_tarball_path )
|
||||
{
|
||||
input_tarball_path = print_string("%s/%s.tar.xz", tmp_root, package_name);
|
||||
if ( fork_and_wait_or_death() )
|
||||
{
|
||||
char* work_dir = dirname(strdup(srctix_path));
|
||||
char* subdir_name = dirname(strdup(srctix_path));
|
||||
if ( chdir(work_dir) != 0 )
|
||||
error(1, errno, "chdir: `%s'", work_dir);
|
||||
const char* cmd_argv[] =
|
||||
{
|
||||
tar,
|
||||
"--create",
|
||||
"--xz",
|
||||
"--directory", input_normalized_path,
|
||||
"--file", input_tarball_path,
|
||||
"--",
|
||||
subdir_name,
|
||||
NULL,
|
||||
};
|
||||
execvp(cmd_argv[0], (char* const*) cmd_argv);
|
||||
error(127, errno, "%s", cmd_argv[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// Copy the normalized directory (if one exists) to the temporary root.
|
||||
if ( input_normalized_path )
|
||||
{
|
||||
if ( fork_and_wait_or_death() )
|
||||
{
|
||||
const char* cmd_argv[] =
|
||||
{
|
||||
cp,
|
||||
"-HRT",
|
||||
"--preserve=timestamps,links",
|
||||
"--",
|
||||
input_normalized_path,
|
||||
normalized_path,
|
||||
NULL,
|
||||
};
|
||||
execvp(cmd_argv[0], (char* const*) cmd_argv);
|
||||
error(127, errno, "%s", cmd_argv[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// There is no input normalized directory, so just extract the tarball here.
|
||||
else
|
||||
{
|
||||
if ( fork_and_wait_or_death() )
|
||||
{
|
||||
const char* cmd_argv[] =
|
||||
{
|
||||
tar,
|
||||
"--extract",
|
||||
"--directory", normalized_path,
|
||||
"--file", input_tarball_path,
|
||||
"--strip-components=1",
|
||||
NULL,
|
||||
};
|
||||
execvp(cmd_argv[0], (char* const*) cmd_argv);
|
||||
error(127, errno, "%s", cmd_argv[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// Copy the tarball into the port tix.
|
||||
char* porttix_tarball_path = print_string("%s/%s", porttix_path, tarball_basename);
|
||||
if ( fork_and_wait_or_death() )
|
||||
{
|
||||
const char* cmd_argv[] =
|
||||
{
|
||||
cp,
|
||||
"--",
|
||||
input_tarball_path,
|
||||
porttix_tarball_path,
|
||||
NULL,
|
||||
};
|
||||
execvp(cmd_argv[0], (char* const*) cmd_argv);
|
||||
error(127, errno, "%s", cmd_argv[0]);
|
||||
}
|
||||
fprintf(porttixinfo_fp, "tar_extract %s\n", tarball_basename);
|
||||
|
||||
// Create the normalization patch.
|
||||
int normalized_fd = open(normalized_path, O_RDONLY | O_DIRECTORY);
|
||||
if ( normalized_fd < 0 )
|
||||
error(1, errno, "`%s'", normalized_path);
|
||||
|
||||
char* patch_normalize_path = join_paths(porttix_path, "patch.normalize");
|
||||
FILE* patch_normalize_fp = fopen(patch_normalize_path, "w");
|
||||
if ( !patch_normalize_fp )
|
||||
error(1, errno, "`%s'", patch_normalize_path);
|
||||
|
||||
int pipes[2];
|
||||
if ( pipe(pipes) )
|
||||
error(1, errno, "pipe");
|
||||
pid_t tar_pid = fork_or_death();
|
||||
if ( !tar_pid )
|
||||
{
|
||||
dup2(pipes[1], 1);
|
||||
close(pipes[1]);
|
||||
close(pipes[0]);
|
||||
const char* cmd_argv[] =
|
||||
{
|
||||
tar,
|
||||
"--list",
|
||||
"--file", porttix_tarball_path,
|
||||
"--strip-components=1",
|
||||
NULL
|
||||
};
|
||||
execvp(cmd_argv[0], (char* const*) cmd_argv);
|
||||
error(127, errno, "%s", cmd_argv[0]);
|
||||
}
|
||||
close(pipes[1]);
|
||||
FILE* tar_fp = fdopen(pipes[0], "r");
|
||||
|
||||
char* line = NULL;
|
||||
size_t line_size = 0;
|
||||
ssize_t line_len;
|
||||
while ( 0 < (line_len = getline(&line, &line_size, tar_fp)) )
|
||||
{
|
||||
if ( line_len && line[line_len-1] == '\n' )
|
||||
line[--line_len] = '\0';
|
||||
const char* path = line;
|
||||
while ( *path != '/' )
|
||||
path++;
|
||||
if ( *path == '/' )
|
||||
path++;
|
||||
if ( !*path )
|
||||
continue;
|
||||
struct stat st;
|
||||
if ( fstatat(normalized_fd, path, &st, 0) != 0 && errno == ENOENT )
|
||||
{
|
||||
fprintf(patch_normalize_fp, "rm -rf -- '");
|
||||
for ( size_t i = 0; path[i]; i++ )
|
||||
if ( path[i] == '\'' )
|
||||
fprintf(patch_normalize_fp, "'\\''");
|
||||
else
|
||||
fputc(path[i], patch_normalize_fp);
|
||||
fprintf(patch_normalize_fp, "'\n");
|
||||
}
|
||||
}
|
||||
free(line);
|
||||
|
||||
fclose(tar_fp);
|
||||
int tar_exit_status;
|
||||
waitpid(tar_pid, &tar_exit_status, 0);
|
||||
if ( !WIFEXITED(tar_exit_status) || WEXITSTATUS(tar_exit_status) != 0 )
|
||||
{
|
||||
error(1, 0, "Unable to list contents of `%s'.", porttix_tarball_path);
|
||||
exit(WEXITSTATUS(tar_exit_status));
|
||||
}
|
||||
|
||||
fclose(patch_normalize_fp);
|
||||
free(patch_normalize_path);
|
||||
fprintf(porttixinfo_fp, "apply_normalize patch.normalize\n");
|
||||
|
||||
close(normalized_fd);
|
||||
|
||||
// Create the patch between the source tix and the normalized tree.
|
||||
char* patch_path = join_paths(porttix_path, "patch.patch");
|
||||
if ( fork_and_wait_or_death(false) )
|
||||
{
|
||||
close(1);
|
||||
if ( open(patch_path, O_WRONLY | O_CREAT | O_TRUNC, 0666) != 1 )
|
||||
error(1, errno, "`%s'", patch_path);
|
||||
if ( chdir(tmp_root) != 0 )
|
||||
error(1, errno, "chdir(`%s')", tmp_root);
|
||||
const char* cmd_argv[] =
|
||||
{
|
||||
diff,
|
||||
"--no-dereference",
|
||||
"-Naur",
|
||||
"--",
|
||||
rel_normalized_path,
|
||||
rel_srctix_path,
|
||||
NULL,
|
||||
};
|
||||
execvp(cmd_argv[0], (char* const*) cmd_argv);
|
||||
error(127, errno, "%s", cmd_argv[0]);
|
||||
}
|
||||
free(patch_path);
|
||||
fprintf(porttixinfo_fp, "apply_patch patch.patch\n");
|
||||
|
||||
// Created the execpatch between the source tix and the normalized tree.
|
||||
char* patch_exec_path = join_paths(porttix_path, "patch.execpatch");
|
||||
if ( fork_and_wait_or_death(false) )
|
||||
{
|
||||
if ( redirect(patch_exec_path, O_WRONLY | O_CREAT | O_TRUNC, 0666) != 0 )
|
||||
error(1, errno, "`%s'", patch_exec_path);
|
||||
if ( chdir(tmp_root) != 0 )
|
||||
error(1, errno, "chdir(`%s')", tmp_root);
|
||||
const char* cmd_argv[] =
|
||||
{
|
||||
tix_execdiff,
|
||||
"--",
|
||||
rel_normalized_path,
|
||||
rel_srctix_path,
|
||||
NULL,
|
||||
};
|
||||
execvp(cmd_argv[0], (char* const*) cmd_argv);
|
||||
error(127, errno, "%s", cmd_argv[0]);
|
||||
}
|
||||
free(patch_exec_path);
|
||||
fprintf(porttixinfo_fp, "apply_execpatch patch.execpatch\n");
|
||||
|
||||
// Close the porttixinfo file.
|
||||
fclose(porttixinfo_fp);
|
||||
free(porttixinfo_path);
|
||||
|
||||
// Package up the output archived port tix.
|
||||
if ( fork_and_wait_or_death() )
|
||||
{
|
||||
const char* cmd_argv[] =
|
||||
{
|
||||
tar,
|
||||
"--create",
|
||||
"--xz",
|
||||
"--directory", tmp_root,
|
||||
"--file", output,
|
||||
"--",
|
||||
package_name,
|
||||
NULL,
|
||||
};
|
||||
execvp(cmd_argv[0], (char* const*) cmd_argv);
|
||||
error(127, errno, "%s", cmd_argv[0]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,321 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2013.
|
||||
|
||||
This file is part of Tix.
|
||||
|
||||
Tix is free software: you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
Tix is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Tix. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
srctix-create.cpp
|
||||
Converts an archived port tix into an archived source tix.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#define __STDC_CONSTANT_MACROS
|
||||
#define __STDC_LIMIT_MACROS
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <error.h>
|
||||
#include <fcntl.h>
|
||||
#include <libgen.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
void help(FILE* fp, const char* argv0)
|
||||
{
|
||||
fprintf(fp, "Usage: %s [OPTION]... PORT-TIX\n", argv0);
|
||||
fprintf(fp, "Converts an archived port tix into an archived source tix.\n");
|
||||
}
|
||||
|
||||
void version(FILE* fp, const char* argv0)
|
||||
{
|
||||
help(fp, argv0);
|
||||
}
|
||||
|
||||
bool is_file_name(const char* path)
|
||||
{
|
||||
return !(strchr(path, '/') || !strcmp(path, ".") || !strcmp(path, ".."));
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
char* output_directory = strdup(".");
|
||||
char* output = NULL;
|
||||
char* patch = strdup(getenv_def("PATCH", "patch"));
|
||||
char* tar = strdup(getenv_def("TAR", "tar"));
|
||||
char* tix_execpatch = strdup(getenv_def("TIX_EXECPATCH", "tix-execpatch"));
|
||||
char* tix_rmpatch = strdup(getenv_def("TIX_RMPATCH", "tix-rmpatch"));
|
||||
char* tmp = strdup(getenv_def("TMP", "/tmp"));
|
||||
|
||||
const char* argv0 = argv[0];
|
||||
for ( int i = 0; i < argc; i++ )
|
||||
{
|
||||
const char* arg = argv[i];
|
||||
if ( arg[0] != '-' )
|
||||
continue;
|
||||
argv[i] = NULL;
|
||||
if ( !strcmp(arg, "--") )
|
||||
break;
|
||||
if ( arg[1] != '-' )
|
||||
{
|
||||
while ( char c = *++arg ) switch ( c )
|
||||
{
|
||||
default:
|
||||
fprintf(stderr, "%s: unknown option -- `%c'\n", argv0, c);
|
||||
help(stderr, argv0);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
else if ( !strcmp(arg, "--help") ) { help(stdout, argv0); exit(0); }
|
||||
else if ( !strcmp(arg, "--version") ) { version(stdout, argv0); exit(0); }
|
||||
else if ( GET_OPTION_VARIABLE("--output-directory", &output_directory) ) { }
|
||||
else if ( GET_OPTION_VARIABLE("--output", &output) ) { }
|
||||
else if ( GET_OPTION_VARIABLE("--patch", &patch) ) { }
|
||||
else if ( GET_OPTION_VARIABLE("--tar", &tar) ) { }
|
||||
else if ( GET_OPTION_VARIABLE("--tix-execpatch", &tix_execpatch) ) { }
|
||||
else if ( GET_OPTION_VARIABLE("--tix-rmpatch", &tix_rmpatch) ) { }
|
||||
else if ( GET_OPTION_VARIABLE("--tmp", &tmp) ) { }
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "%s: unknown option: `%s'\n", argv0, arg);
|
||||
help(stderr, argv0);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if ( argc == 1 )
|
||||
{
|
||||
help(stdout, argv0);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
CompactArguments(&argc, &argv);
|
||||
|
||||
if ( argc <= 1 )
|
||||
{
|
||||
fprintf(stderr, "%s: no archived port tix specified\n", argv0);
|
||||
help(stderr, argv0);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if ( 3 <= argc )
|
||||
{
|
||||
fprintf(stderr, "%s: unexpected extra operand `%s'\n", argv0, argv[2]);
|
||||
help(stderr, argv0);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
const char* porttix_path = argv[1];
|
||||
|
||||
char* tmp_in_root = print_string("%s/tmppid.%ju.in", tmp, (uintmax_t) getpid());
|
||||
if ( mkdir_p(tmp_in_root, 0777) != 0 )
|
||||
error(1, errno, "mkdir: `%s'", tmp_in_root);
|
||||
on_exit(cleanup_file_or_directory, tmp_in_root);
|
||||
|
||||
if ( fork_and_wait_or_death() )
|
||||
{
|
||||
const char* cmd_argv[] =
|
||||
{
|
||||
tar,
|
||||
"--extract",
|
||||
"--directory", tmp_in_root,
|
||||
"--file", porttix_path,
|
||||
"--strip-components=1",
|
||||
NULL,
|
||||
};
|
||||
execvp(cmd_argv[0], (char* const*) cmd_argv);
|
||||
error(127, errno, "%s", cmd_argv[0]);
|
||||
}
|
||||
|
||||
char* porttixinfo_path = join_paths(tmp_in_root, "porttixinfo");
|
||||
FILE* porttixinfo_fp = fopen(porttixinfo_path, "r");
|
||||
if ( !porttixinfo_fp )
|
||||
{
|
||||
if ( errno == ENOENT )
|
||||
error(0, 0, "`%s' doesn't appear to be an archived port tix",
|
||||
porttix_path);
|
||||
error(1, errno, "`%s'", porttixinfo_path);
|
||||
}
|
||||
|
||||
char* tmp_out_root = print_string("%s/tmppid.%ju.out", tmp, (uintmax_t) getpid());
|
||||
if ( mkdir_p(tmp_out_root, 0777) != 0 )
|
||||
error(1, errno, "mkdir: `%s'", tmp_out_root);
|
||||
on_exit(cleanup_file_or_directory, tmp_out_root);
|
||||
|
||||
char* package_name = NULL;
|
||||
char* srctix_path = NULL;
|
||||
|
||||
char* line = NULL;
|
||||
size_t line_size = 0;
|
||||
ssize_t line_len;
|
||||
while ( 0 < (line_len = getline(&line, &line_size, porttixinfo_fp)) )
|
||||
{
|
||||
if ( line_len && line[line_len-1] == '\n' )
|
||||
line[--line_len] = '\0';
|
||||
char* first_space = strchr(line, ' ');
|
||||
if ( !first_space )
|
||||
error(1, errno, "`%s`: malformed line `%s'",
|
||||
porttixinfo_path, line);
|
||||
*first_space = '\0';
|
||||
const char* function = line;
|
||||
const char* parameter = first_space + 1;
|
||||
|
||||
if ( !strcmp(function, "package_name") )
|
||||
{
|
||||
if ( package_name )
|
||||
error(1, errno, "`%s`: unexpected additional package name `%s'",
|
||||
porttixinfo_path, parameter);
|
||||
if ( !is_file_name(parameter) )
|
||||
error(1, errno, "`%s`: malformed package name `%s'",
|
||||
porttixinfo_path, parameter);
|
||||
package_name = strdup(parameter);
|
||||
srctix_path = join_paths(tmp_out_root, package_name);
|
||||
if ( mkdir_p(srctix_path, 0777) != 0 )
|
||||
error(1, errno, "mkdir: `%s'", srctix_path);
|
||||
}
|
||||
else if ( !package_name )
|
||||
error(1, errno, "`%s`: expected package name before `%s'",
|
||||
porttixinfo_path, function);
|
||||
else if ( !strcmp(function, "tar_extract") )
|
||||
{
|
||||
if ( !is_file_name(parameter) )
|
||||
error(1, errno, "`%s`: malformed tarball filename `%s'",
|
||||
porttixinfo_path, parameter);
|
||||
char* tarball_path = join_paths(tmp_in_root, parameter);
|
||||
if ( fork_and_wait_or_death() )
|
||||
{
|
||||
const char* cmd_argv[] =
|
||||
{
|
||||
tar,
|
||||
"--extract",
|
||||
"--directory", srctix_path,
|
||||
"--file", tarball_path,
|
||||
"--strip-components=1",
|
||||
NULL,
|
||||
};
|
||||
execvp(cmd_argv[0], (char* const*) cmd_argv);
|
||||
error(127, errno, "%s", cmd_argv[0]);
|
||||
}
|
||||
free(tarball_path);
|
||||
}
|
||||
else if ( !strcmp(function, "apply_normalize") )
|
||||
{
|
||||
if ( !is_file_name(parameter) )
|
||||
error(1, errno, "`%s`: malformed normalize filename `%s'",
|
||||
porttixinfo_path, parameter);
|
||||
char* rmpatch_path = join_paths(tmp_in_root, parameter);
|
||||
if ( fork_and_wait_or_death() )
|
||||
{
|
||||
const char* cmd_argv[] =
|
||||
{
|
||||
tix_rmpatch,
|
||||
"--directory", srctix_path,
|
||||
"--",
|
||||
rmpatch_path,
|
||||
NULL,
|
||||
};
|
||||
execvp(cmd_argv[0], (char* const*) cmd_argv);
|
||||
error(127, errno, "%s", cmd_argv[0]);
|
||||
}
|
||||
free(rmpatch_path);
|
||||
}
|
||||
else if ( !strcmp(function, "apply_patch") )
|
||||
{
|
||||
if ( !is_file_name(parameter) )
|
||||
error(1, errno, "`%s`: malformed patch filename `%s'",
|
||||
porttixinfo_path, parameter);
|
||||
char* patch_path = join_paths(tmp_in_root, parameter);
|
||||
if ( fork_and_wait_or_death() )
|
||||
{
|
||||
const char* cmd_argv[] =
|
||||
{
|
||||
patch,
|
||||
"--strip=1",
|
||||
"--silent",
|
||||
"--directory", srctix_path,
|
||||
"--input", patch_path,
|
||||
NULL,
|
||||
};
|
||||
execvp(cmd_argv[0], (char* const*) cmd_argv);
|
||||
error(127, errno, "%s", cmd_argv[0]);
|
||||
}
|
||||
free(patch_path);
|
||||
}
|
||||
else if ( !strcmp(function, "apply_execpatch") )
|
||||
{
|
||||
if ( !is_file_name(parameter) )
|
||||
error(1, errno, "`%s`: malformed execpatch filename `%s'",
|
||||
porttixinfo_path, parameter);
|
||||
char* execpatch_path = join_paths(tmp_in_root, parameter);
|
||||
if ( fork_and_wait_or_death() )
|
||||
{
|
||||
const char* cmd_argv[] =
|
||||
{
|
||||
tix_execpatch,
|
||||
"--directory", srctix_path,
|
||||
"--",
|
||||
execpatch_path,
|
||||
NULL,
|
||||
};
|
||||
execvp(cmd_argv[0], (char* const*) cmd_argv);
|
||||
error(127, errno, "%s", cmd_argv[0]);
|
||||
}
|
||||
free(execpatch_path);
|
||||
}
|
||||
else
|
||||
error(1, errno, "`%s`: unsupported function `%s'",
|
||||
porttixinfo_path, function);
|
||||
}
|
||||
free(line);
|
||||
|
||||
fclose(porttixinfo_fp);
|
||||
free(porttixinfo_path);
|
||||
|
||||
if ( !output )
|
||||
output = print_string("%s/%s.srctix.tar.xz", output_directory, package_name);
|
||||
|
||||
if ( fork_and_wait_or_death() )
|
||||
{
|
||||
const char* cmd_argv[] =
|
||||
{
|
||||
tar,
|
||||
"--create",
|
||||
"--xz",
|
||||
"--directory", tmp_out_root,
|
||||
"--file", output,
|
||||
"--",
|
||||
package_name,
|
||||
NULL,
|
||||
};
|
||||
execvp(cmd_argv[0], (char* const*) cmd_argv);
|
||||
error(127, errno, "%s", cmd_argv[0]);
|
||||
}
|
||||
|
||||
free(srctix_path);
|
||||
free(package_name);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,713 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2013.
|
||||
|
||||
This file is part of Tix.
|
||||
|
||||
Tix is free software: you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
Tix is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Tix. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
tix-build.cpp
|
||||
Compile a source tix into a tix suitable for installation.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#define __STDC_CONSTANT_MACROS
|
||||
#define __STDC_LIMIT_MACROS
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <error.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
enum build_step
|
||||
{
|
||||
BUILD_STEP_NO_SUCH_STEP,
|
||||
BUILD_STEP_START,
|
||||
BUILD_STEP_PRE_CLEAN,
|
||||
BUILD_STEP_CONFIGURE,
|
||||
BUILD_STEP_BUILD,
|
||||
BUILD_STEP_INSTALL,
|
||||
BUILD_STEP_POST_INSTALL,
|
||||
BUILD_STEP_PACKAGE,
|
||||
BUILD_STEP_POST_CLEAN,
|
||||
BUILD_STEP_END,
|
||||
};
|
||||
|
||||
bool should_do_build_step(enum build_step step,
|
||||
enum build_step start,
|
||||
enum build_step end)
|
||||
{
|
||||
return start <= step && step <= end;
|
||||
}
|
||||
|
||||
#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)
|
||||
{
|
||||
if ( !strcmp(step_name, "start") )
|
||||
return BUILD_STEP_START;
|
||||
if ( !strcmp(step_name, "pre-clean") )
|
||||
return BUILD_STEP_PRE_CLEAN;
|
||||
if ( !strcmp(step_name, "configure") )
|
||||
return BUILD_STEP_CONFIGURE;
|
||||
if ( !strcmp(step_name, "build") )
|
||||
return BUILD_STEP_BUILD;
|
||||
if ( !strcmp(step_name, "install") )
|
||||
return BUILD_STEP_INSTALL;
|
||||
if ( !strcmp(step_name, "post-install") )
|
||||
return BUILD_STEP_POST_INSTALL;
|
||||
if ( !strcmp(step_name, "post-clean") )
|
||||
return BUILD_STEP_POST_CLEAN;
|
||||
if ( !strcmp(step_name, "package") )
|
||||
return BUILD_STEP_PACKAGE;
|
||||
if ( !strcmp(step_name, "clean") )
|
||||
return BUILD_STEP_POST_CLEAN;
|
||||
if ( !strcmp(step_name, "end") )
|
||||
return BUILD_STEP_END;
|
||||
return BUILD_STEP_NO_SUCH_STEP;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char* build;
|
||||
char* build_dir;
|
||||
char* destination;
|
||||
char* host;
|
||||
char* make;
|
||||
char* makeflags;
|
||||
char* package_dir;
|
||||
char* package_info_path;
|
||||
char* package_name;
|
||||
char* prefix;
|
||||
char* sysroot;
|
||||
char* tar;
|
||||
char* target;
|
||||
char* tmp;
|
||||
string_array_t package_info;
|
||||
enum build_step start_step;
|
||||
enum build_step end_step;
|
||||
} metainfo_t;
|
||||
|
||||
void emit_pkg_config_wrapper(metainfo_t* minfo)
|
||||
{
|
||||
char* bindir = print_string("%s/tmppid.%ju.bin", minfo->tmp, (uintmax_t) getpid());
|
||||
if ( mkdir_p(bindir, 0777) != 0 )
|
||||
error(1, errno, "mkdir: `%s'", bindir);
|
||||
|
||||
on_exit(cleanup_file_or_directory, strdup(bindir));
|
||||
|
||||
// Create a pkg-config script for the build system.
|
||||
char* pkg_config_for_build_path = print_string("%s/build-pkg-config", bindir);
|
||||
FILE* pkg_config_for_build = fopen(pkg_config_for_build_path, "w");
|
||||
if ( !pkg_config_for_build )
|
||||
error(1, errno, "`%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"));
|
||||
fprintf(pkg_config_for_build, "exec ${PKG_CONFIG:-pkg-config} \"$@\"\n");
|
||||
fflush(pkg_config_for_build);
|
||||
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);
|
||||
FILE* pkg_config = fopen(pkg_config_path, "w");
|
||||
if ( !pkg_config )
|
||||
error(1, errno, "`%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");
|
||||
fflush(pkg_config);
|
||||
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/%s/lib/pkgconfig",
|
||||
minfo->sysroot, minfo->prefix, minfo->host);
|
||||
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);
|
||||
|
||||
char* new_path = print_string("%s:%s", bindir, getenv("PATH") ? getenv("PATH") : "");
|
||||
setenv("PATH", new_path, 1);
|
||||
free(new_path);
|
||||
|
||||
free(bindir);
|
||||
}
|
||||
|
||||
void Configure(metainfo_t* minfo)
|
||||
{
|
||||
if ( fork_and_wait_or_recovery() )
|
||||
{
|
||||
string_array_t* pkg_info = &minfo->package_info;
|
||||
const char* configure_raw =
|
||||
dictionary_get(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(pkg_info, "pkg.configure.args", "");
|
||||
const char* conf_extra_vars =
|
||||
dictionary_get(pkg_info, "pkg.configure.vars", "");
|
||||
bool with_sysroot =
|
||||
parse_boolean(dictionary_get(pkg_info, "pkg.configure.with-sysroot",
|
||||
"false"));
|
||||
bool with_build_sysroot =
|
||||
parse_boolean(dictionary_get(pkg_info, "pkg.configure.with-build-sysroot",
|
||||
"false"));
|
||||
if ( chdir(minfo->build_dir) != 0 )
|
||||
error(1, errno, "chdir: `%s'", minfo->build_dir);
|
||||
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 )
|
||||
continue;
|
||||
*assignment = '\0';
|
||||
char* value = assignment+1;
|
||||
setenv(key, value, 1);
|
||||
}
|
||||
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/%s", minfo->prefix, minfo->host),
|
||||
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 )
|
||||
string_array_append(&args, "--with-sysroot=/");
|
||||
unsetenv("HOST_SYSTEM_ROOT");
|
||||
}
|
||||
else if ( minfo->sysroot && with_sysroot )
|
||||
{
|
||||
string_array_append(&args, print_string("--with-sysroot=%s",
|
||||
minfo->sysroot));
|
||||
unsetenv("HOST_SYSTEM_ROOT");
|
||||
}
|
||||
else if ( minfo->sysroot )
|
||||
{
|
||||
setenv("HOST_SYSTEM_ROOT", minfo->sysroot, 1);
|
||||
}
|
||||
string_array_append_token_string(&args, conf_extra_args);
|
||||
string_array_append(&args, NULL);
|
||||
recovery_execvp(args.strings[0], (char* const*) args.strings);
|
||||
error(127, errno, "`%s'", args.strings[0]);
|
||||
}
|
||||
}
|
||||
|
||||
void Make(metainfo_t* minfo, const char* make_target,
|
||||
const char* destdir = NULL, bool die_on_error = true,
|
||||
const char* subdir = NULL)
|
||||
{
|
||||
if ( (!die_on_error && fork_and_wait_or_death(die_on_error)) ||
|
||||
(die_on_error && fork_and_wait_or_recovery()) )
|
||||
{
|
||||
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(pkg_info, "pkg.make.args", "");
|
||||
const char* make_extra_vars = dictionary_get(pkg_info, "pkg.make.vars", "");
|
||||
if ( override_make )
|
||||
{
|
||||
free(make);
|
||||
make = join_paths(minfo->package_dir, override_make);
|
||||
}
|
||||
if ( dictionary_get(pkg_info, "pkg.make.needed-vars.CC", NULL) )
|
||||
setenv("CC", strcmp(minfo->build, minfo->host) ?
|
||||
print_string("%s-gcc", minfo->host) : "gcc", 1);
|
||||
if ( dictionary_get(pkg_info, "pkg.make.needed-vars.CXX", NULL) )
|
||||
setenv("CXX", strcmp(minfo->build, minfo->host) ?
|
||||
print_string("%s-g++", minfo->host) : "g++", 1);
|
||||
bool with_sysroot =
|
||||
parse_boolean(dictionary_get(pkg_info, "pkg.configure.with-sysroot",
|
||||
"false"));
|
||||
bool with_build_sysroot =
|
||||
parse_boolean(dictionary_get(pkg_info, "pkg.configure.with-build-sysroot",
|
||||
"false"));
|
||||
if ( chdir(minfo->build_dir) != 0 )
|
||||
error(1, errno, "chdir: `%s'", minfo->build_dir);
|
||||
if ( subdir && chdir(subdir) != 0 )
|
||||
error(1, errno, "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),
|
||||
setenv("EXEC_PREFIX", join_paths(minfo->prefix, minfo->host), 1);
|
||||
else
|
||||
unsetenv("PREFIX"),
|
||||
unsetenv("EXEC_PREFIX");
|
||||
if ( !(with_sysroot || with_build_sysroot) && minfo->sysroot )
|
||||
setenv("HOST_SYSTEM_ROOT", minfo->sysroot, 1);
|
||||
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 )
|
||||
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);
|
||||
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);
|
||||
error(127, errno, "`%s'", args.strings[0]);
|
||||
}
|
||||
}
|
||||
|
||||
void BuildPackage(metainfo_t* minfo)
|
||||
{
|
||||
// Detect which build system we are interfacing with.
|
||||
string_array_t* pinfo = &minfo->package_info;
|
||||
const char* build_system = dictionary_get(pinfo, "pkg.build-system");
|
||||
assert(build_system);
|
||||
|
||||
// Determine whether need to do an out-of-directory build.
|
||||
const char* use_build_dir_var =
|
||||
dictionary_get(pinfo, "pkg.configure.use-build-directory", "false");
|
||||
bool use_build_dir = parse_boolean(use_build_dir_var);
|
||||
if ( use_build_dir )
|
||||
{
|
||||
minfo->build_dir = print_string("%s/tmppid.%ju", minfo->tmp,
|
||||
(uintmax_t) getpid());
|
||||
if ( mkdir_p(minfo->build_dir, 0777) != 0 )
|
||||
error(1, errno, "mkdir: `%s'", minfo->build_dir);
|
||||
}
|
||||
else
|
||||
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(pinfo, "pkg.make.clean-target",
|
||||
default_clean_target);
|
||||
const char* ignore_clean_failure_var =
|
||||
dictionary_get(pinfo, "pkg.make.ignore-clean-failure",
|
||||
!strcmp(build_system, "configure") ? "true" : "false");
|
||||
bool ignore_clean_failure = parse_boolean(ignore_clean_failure_var);
|
||||
|
||||
if ( SHOULD_DO_BUILD_STEP(BUILD_STEP_PRE_CLEAN, minfo) && !use_build_dir )
|
||||
Make(minfo, clean_target, NULL, !ignore_clean_failure);
|
||||
|
||||
// Configure the build directory if needed.
|
||||
if ( strcmp(build_system, "configure") == 0 &&
|
||||
SHOULD_DO_BUILD_STEP(BUILD_STEP_CONFIGURE, minfo) )
|
||||
Configure(minfo);
|
||||
|
||||
bool location_independent =
|
||||
parse_boolean(dictionary_get(pinfo, "pkg.location-independent", "false"));
|
||||
|
||||
const char* subdir = dictionary_get(pinfo, "pkg.subdir", NULL);
|
||||
|
||||
const char* build_target = dictionary_get(pinfo, "pkg.make.build-target", "all");
|
||||
const char* install_target = dictionary_get(pinfo, "pkg.make.install-target", "install");
|
||||
|
||||
if ( !location_independent && !minfo->prefix )
|
||||
error(1, 0, "error: %s is not location independent and you need to "
|
||||
"specify the intended destination prefix using --prefix or "
|
||||
"PREFIX", minfo->package_name);
|
||||
|
||||
if ( SHOULD_DO_BUILD_STEP(BUILD_STEP_BUILD, minfo) )
|
||||
Make(minfo, build_target, NULL, true, subdir);
|
||||
|
||||
char* tardir_rel = print_string("%s/%s", minfo->tmp, "tmp-tixbuild");
|
||||
char* destdir_rel = print_string("%s/%s", minfo->tmp, "tmp-tixbuild/data");
|
||||
char* tixdir_rel = print_string("%s/%s", minfo->tmp, "tmp-tixbuild/tix");
|
||||
char* tixinfo_rel = print_string("%s/%s", minfo->tmp, "tmp-tixbuild/tix/tixinfo");
|
||||
|
||||
while ( mkdir(tardir_rel, 0777) != 0 )
|
||||
{
|
||||
if ( errno != EEXIST )
|
||||
error(1, errno, "mkdir: `%s'", tardir_rel);
|
||||
if ( rmdir(tardir_rel) != 0 )
|
||||
error(1, errno, "rmdir: `%s'", tardir_rel);
|
||||
}
|
||||
|
||||
if ( mkdir(destdir_rel, 0777) != 0 )
|
||||
error(1, errno, "mkdir: `%s'", destdir_rel);
|
||||
if ( mkdir(tixdir_rel, 0777) != 0 )
|
||||
error(1, errno, "mkdir: `%s'", tixdir_rel);
|
||||
|
||||
char* destdir = canonicalize_file_name(destdir_rel);
|
||||
if ( !destdir )
|
||||
error(1, errno, "canonicalize_file_name: `%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 )
|
||||
error(1, errno, "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);
|
||||
setenv("BUILD", minfo->build, 1);
|
||||
setenv("HOST", minfo->host, 1);
|
||||
setenv("TARGET", minfo->target, 1);
|
||||
if ( minfo->prefix )
|
||||
setenv("PREFIX", minfo->prefix, 1),
|
||||
setenv("EXEC_PREFIX", join_paths(minfo->prefix, minfo->host), 1);
|
||||
else
|
||||
unsetenv("PREFIX"),
|
||||
unsetenv("EXEC_PREFIX");
|
||||
const char* cmd_argv[] =
|
||||
{
|
||||
post_install_cmd,
|
||||
NULL
|
||||
};
|
||||
recovery_execvp(cmd_argv[0], (char* const*) cmd_argv);
|
||||
error(127, errno, "%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 )
|
||||
error(1, errno, "`%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) )
|
||||
error(1, errno, "write: `%s'", tixinfo_rel);
|
||||
|
||||
fclose(tixinfo_fp);
|
||||
|
||||
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",
|
||||
"--file", package_tix,
|
||||
"tix",
|
||||
"data",
|
||||
NULL
|
||||
};
|
||||
recovery_execvp(cmd_argv[0], (char* const*) cmd_argv);
|
||||
error(127, errno, "%s", cmd_argv[0]);
|
||||
}
|
||||
}
|
||||
|
||||
unlink(tixinfo_rel);
|
||||
rmdir(destdir_rel);
|
||||
rmdir(tixdir_rel);
|
||||
rmdir(tardir_rel);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void VerifySourceTixInformation(metainfo_t* minfo)
|
||||
{
|
||||
const char* pipath = minfo->package_info_path;
|
||||
string_array_t* pinfo = &minfo->package_info;
|
||||
const char* tix_version = VerifyInfoVariable(pinfo, "tix.version", pipath);
|
||||
if ( atoi(tix_version) != 1 )
|
||||
error(1, 0, "error: `%s': tix version `%s' not supported", pipath,
|
||||
tix_version);
|
||||
const char* tix_class = VerifyInfoVariable(pinfo, "tix.class", pipath);
|
||||
if ( !strcmp(tix_class, "tix") )
|
||||
error(1, 0, "error: `%s': this object is a binary tix and is already "
|
||||
"compiled.\n", pipath);
|
||||
if ( strcmp(tix_class, "srctix") )
|
||||
error(1, 0, "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);
|
||||
}
|
||||
|
||||
// TODO: The MAKEFLAGS variable is actually not in the same format as the token
|
||||
// string language. It appears that GNU make doesn't escape " characters,
|
||||
// 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()
|
||||
{
|
||||
const char* makeflags_environment = getenv("MAKEFLAGS");
|
||||
if ( !makeflags_environment )
|
||||
return;
|
||||
string_array_t makeflags = string_array_make();
|
||||
string_array_append_token_string(&makeflags, makeflags_environment);
|
||||
for ( size_t i = 0; i < makeflags.length; i++ )
|
||||
{
|
||||
char* flag = makeflags.strings[i];
|
||||
assert(flag);
|
||||
if ( flag[0] == '-' )
|
||||
continue;
|
||||
if ( !strchr(flag, '=') )
|
||||
continue;
|
||||
free(flag);
|
||||
for ( size_t n = i + 1; n < makeflags.length; n++ )
|
||||
makeflags.strings[n-1] = makeflags.strings[n];
|
||||
makeflags.length--;
|
||||
}
|
||||
char* new_makeflags_environment = token_string_of_string_array(&makeflags);
|
||||
assert(new_makeflags_environment);
|
||||
setenv("MAKEFLAGS", new_makeflags_environment, 1);
|
||||
free(new_makeflags_environment);
|
||||
string_array_reset(&makeflags);
|
||||
}
|
||||
|
||||
void Usage(FILE* fp, const char* argv0)
|
||||
{
|
||||
fprintf(fp, "Usage: %s [OPTION]... PACKAGE\n", argv0);
|
||||
fprintf(fp, "Compile a source tix into a tix suitable for installation.\n");
|
||||
}
|
||||
|
||||
void Help(FILE* fp, const char* argv0)
|
||||
{
|
||||
Usage(fp, argv0);
|
||||
}
|
||||
|
||||
void Version(FILE* fp, const char* argv0)
|
||||
{
|
||||
Usage(fp, argv0);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
metainfo_t minfo;
|
||||
minfo.build = NULL;
|
||||
minfo.destination = strdup(getenv_def("TIX_BUILD_DESTINATION", "."));
|
||||
minfo.host = NULL;
|
||||
minfo.makeflags = strdup_null(getenv_def("MAKEFLAGS", NULL));
|
||||
minfo.make = strdup(getenv_def("MAKE", "make"));
|
||||
minfo.prefix = strdup_null(getenv_def("PREFIX", NULL));
|
||||
minfo.sysroot = strdup_null(getenv_def("HOST_SYSTEM_ROOT", NULL));
|
||||
minfo.target = NULL;
|
||||
minfo.tar = strdup(getenv_def("TAR", "tar"));
|
||||
minfo.tmp = strdup(getenv_def("BUILDTMP", "."));
|
||||
char* start_step_string = strdup("start");
|
||||
char* end_step_string = strdup("end");
|
||||
|
||||
const char* argv0 = argv[0];
|
||||
for ( int i = 0; i < argc; i++ )
|
||||
{
|
||||
const char* arg = argv[i];
|
||||
if ( arg[0] != '-' )
|
||||
continue;
|
||||
argv[i] = NULL;
|
||||
if ( !strcmp(arg, "--") )
|
||||
break;
|
||||
if ( arg[1] != '-' )
|
||||
{
|
||||
while ( char c = *++arg ) switch ( c )
|
||||
{
|
||||
default:
|
||||
fprintf(stderr, "%s: unknown option -- `%c'\n", argv0, c);
|
||||
Usage(stderr, argv0);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
else if ( !strcmp(arg, "--help") ) { Help(stdout, argv0); exit(0); }
|
||||
else if ( !strcmp(arg, "--usage") ) { Usage(stdout, argv0); exit(0); }
|
||||
else if ( !strcmp(arg, "--version") ) { Version(stdout, argv0); exit(0); }
|
||||
else if ( GET_OPTION_VARIABLE("--build", &minfo.build) ) { }
|
||||
else if ( GET_OPTION_VARIABLE("--destination", &minfo.destination) ) { }
|
||||
else if ( GET_OPTION_VARIABLE("--end", &end_step_string) ) { }
|
||||
else if ( GET_OPTION_VARIABLE("--host", &minfo.host) ) { }
|
||||
else if ( GET_OPTION_VARIABLE("--makeflags", &minfo.makeflags) ) { }
|
||||
else if ( GET_OPTION_VARIABLE("--make", &minfo.make) ) { }
|
||||
else if ( GET_OPTION_VARIABLE("--prefix", &minfo.prefix) ) { }
|
||||
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) ) { }
|
||||
else if ( GET_OPTION_VARIABLE("--tar", &minfo.tar) ) { }
|
||||
else if ( GET_OPTION_VARIABLE("--tmp", &minfo.tmp) ) { }
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "%s: unknown option: `%s'\n", argv0, arg);
|
||||
Usage(stderr, argv0);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if ( !(minfo.start_step = step_of_step_name(start_step_string)) )
|
||||
{
|
||||
fprintf(stderr, "%s: no such step `%s'\n", argv0, start_step_string);
|
||||
Usage(stderr, argv0);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if ( !(minfo.end_step = step_of_step_name(end_step_string)) )
|
||||
{
|
||||
fprintf(stderr, "%s: no such step `%s'\n", argv0, end_step_string);
|
||||
Usage(stderr, argv0);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if ( argc == 1 )
|
||||
{
|
||||
Usage(stdout, argv0);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
CompactArguments(&argc, &argv);
|
||||
|
||||
PurifyMakeflags();
|
||||
|
||||
if ( minfo.prefix && !strcmp(minfo.prefix, "/") )
|
||||
minfo.prefix[0] = '\0';
|
||||
|
||||
if ( argc < 2 )
|
||||
{
|
||||
fprintf(stderr, "%s: no package specified\n", argv0);
|
||||
Usage(stderr, argv0);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if ( 2 < argc )
|
||||
{
|
||||
fprintf(stderr, "%s: unexpected extra operand `%s'\n", argv0, argv[2]);
|
||||
Usage(stderr, argv0);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
minfo.package_dir = canonicalize_file_name(argv[1]);
|
||||
if ( !minfo.package_dir )
|
||||
error(1, errno, "canonicalize_file_name: `%s'", argv[1]);
|
||||
|
||||
if ( !minfo.build && !(minfo.build = GetBuildTriplet()) )
|
||||
error(1, errno, "unable to determine host, use --host or BUILD");
|
||||
if ( !minfo.host && !(minfo.host = strdup_null(getenv("HOST"))) )
|
||||
minfo.host = strdup(minfo.build);
|
||||
if ( !minfo.target && !(minfo.target = strdup_null(getenv("TARGET"))) )
|
||||
minfo.target = strdup(minfo.host);
|
||||
|
||||
if ( !IsDirectory(minfo.package_dir) )
|
||||
error(1, errno, "`%s'", minfo.package_dir);
|
||||
|
||||
minfo.package_info_path = print_string("%s/tixbuildinfo",
|
||||
minfo.package_dir);
|
||||
|
||||
string_array_t* package_info = &(minfo.package_info = string_array_make());
|
||||
if ( !dictionary_append_file_path(package_info, minfo.package_info_path) )
|
||||
{
|
||||
if ( errno == ENOENT )
|
||||
fprintf(stderr, "%s: `%s' doesn't appear to be a source .tix:\n",
|
||||
argv0, minfo.package_dir);
|
||||
error(1, errno, "`%s'", minfo.package_info_path);
|
||||
}
|
||||
|
||||
VerifySourceTixInformation(&minfo);
|
||||
minfo.package_name = strdup(dictionary_get(package_info, "pkg.name"));
|
||||
|
||||
emit_pkg_config_wrapper(&minfo);
|
||||
|
||||
BuildPackage(&minfo);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,175 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2013.
|
||||
|
||||
This file is part of Tix.
|
||||
|
||||
Tix is free software: you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
Tix is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Tix. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
tix-collection.cpp
|
||||
Administer and configure a tix collection.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#define __STDC_CONSTANT_MACROS
|
||||
#define __STDC_LIMIT_MACROS
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <error.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
void Usage(FILE* fp, const char* argv0)
|
||||
{
|
||||
fprintf(fp, "Usage: %s PREFIX [OPTION]... COMMAND\n", argv0);
|
||||
fprintf(fp, "Administer and configure a tix collection.\n");
|
||||
}
|
||||
|
||||
void Help(FILE* fp, const char* argv0)
|
||||
{
|
||||
Usage(fp, argv0);
|
||||
}
|
||||
|
||||
void Version(FILE* fp, const char* argv0)
|
||||
{
|
||||
Usage(fp, argv0);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
char* collection = strdup_null(getenv_def("TIX_COLLECTION", NULL));
|
||||
char* platform = NULL;
|
||||
char* prefix = NULL;
|
||||
|
||||
const char* argv0 = argv[0];
|
||||
for ( int i = 0; i < argc; i++ )
|
||||
{
|
||||
const char* arg = argv[i];
|
||||
if ( arg[0] != '-' )
|
||||
continue;
|
||||
argv[i] = NULL;
|
||||
if ( !strcmp(arg, "--") )
|
||||
break;
|
||||
if ( arg[1] != '-' )
|
||||
{
|
||||
while ( char c = *++arg ) switch ( c )
|
||||
{
|
||||
default:
|
||||
fprintf(stderr, "%s: unknown option -- `%c'\n", argv0, c);
|
||||
Usage(stderr, argv0);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
else if ( !strcmp(arg, "--help") ) { Help(stdout, argv0); exit(0); }
|
||||
else if ( !strcmp(arg, "--usage") ) { Usage(stdout, argv0); exit(0); }
|
||||
else if ( !strcmp(arg, "--version") ) { Version(stdout, argv0); exit(0); }
|
||||
else if ( GET_OPTION_VARIABLE("--collection", &collection) ) { }
|
||||
else if ( GET_OPTION_VARIABLE("--platform", &platform) ) { }
|
||||
else if ( GET_OPTION_VARIABLE("--prefix", &prefix) ) { }
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "%s: unknown option: `%s'\n", argv0, arg);
|
||||
Usage(stderr, argv0);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if ( argc == 1 )
|
||||
{
|
||||
Usage(stdout, argv0);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
CompactArguments(&argc, &argv);
|
||||
|
||||
ParseOptionalCommandLineCollectionPrefix(&collection, &argc, &argv);
|
||||
VerifyCommandLineCollection(&collection);
|
||||
|
||||
if ( !prefix )
|
||||
prefix = strdup(collection);
|
||||
|
||||
if ( argc == 1 )
|
||||
{
|
||||
error(0, 0, "error: no command specified.");
|
||||
Usage(stderr, argv0);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
const char* cmd = argv[1];
|
||||
if ( !strcmp(cmd, "create") )
|
||||
{
|
||||
if ( !platform && !(platform = GetBuildTriplet()) )
|
||||
error(1, errno, "unable to determine platform, use --platform");
|
||||
|
||||
char* tix_path = join_paths(collection, "tix");
|
||||
if ( mkdir_p(tix_path, 0777) != 0 )
|
||||
error(1, errno, "mkdir: `%s'", tix_path);
|
||||
|
||||
char* tixdb_path = join_paths(tix_path, platform);
|
||||
if ( mkdir_p(tixdb_path, 0777) != 0 )
|
||||
error(1, errno, "mkdir: `%s'", tixdb_path);
|
||||
|
||||
char* collection_conf_path = join_paths(tixdb_path, "collection.conf");
|
||||
FILE* conf_fp = fopen(collection_conf_path, "wx");
|
||||
if ( !conf_fp && errno == EEXIST )
|
||||
error(1, 0, "error: `%s' already exists, a tix collection is "
|
||||
"already installed at `%s'.", collection_conf_path,
|
||||
collection);
|
||||
fprintf(conf_fp, "tix.version=1\n");
|
||||
fprintf(conf_fp, "tix.class=collection\n");
|
||||
fprintf(conf_fp, "collection.prefix=%s\n", !strcmp(prefix, "/") ? "" :
|
||||
prefix);
|
||||
fprintf(conf_fp, "collection.platform=%s\n", platform);
|
||||
fclose(conf_fp);
|
||||
free(collection_conf_path);
|
||||
|
||||
const char* repo_list_path = join_paths(tixdb_path, "repository.list");
|
||||
FILE* repo_list_fp = fopen(repo_list_path, "w");
|
||||
if ( !repo_list_fp )
|
||||
error(1, errno, "`%s'", repo_list_path);
|
||||
fclose(repo_list_fp);
|
||||
|
||||
const char* inst_list_path = join_paths(tixdb_path, "installed.list");
|
||||
FILE* inst_list_fp = fopen(inst_list_path, "w");
|
||||
if ( !inst_list_fp )
|
||||
error(1, errno, "`%s'", inst_list_path);
|
||||
fclose(inst_list_fp);
|
||||
|
||||
printf("Created empty tix collection at `%s' with no repositories.\n",
|
||||
collection);
|
||||
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "%s: unknown command: `%s'\n", argv0, cmd);
|
||||
Usage(stderr, argv0);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
#!/bin/sh
|
||||
################################################################################
|
||||
#
|
||||
# Copyright(C) Jonas 'Sortie' Termansen 2013.
|
||||
#
|
||||
# This file is part of Tix.
|
||||
#
|
||||
# Tix is free software: you can redistribute it and/or modify it under the
|
||||
# terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, either version 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# Tix is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# Tix. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# tix-eradicate-libtool-la
|
||||
# Deletes useless libtool .la files from the staging directory.
|
||||
#
|
||||
################################################################################
|
||||
|
||||
set -e
|
||||
cd "$TIX_INSTALL_DIR"
|
||||
find | grep '\.la$' | while read la; do rm -f "$la"; done
|
|
@ -0,0 +1,224 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2013.
|
||||
|
||||
This file is part of Tix.
|
||||
|
||||
Tix is free software: you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
Tix is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Tix. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
tix-execdiff.cpp
|
||||
Reports which files have had the executable bit changed between two trees.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#define __STDC_CONSTANT_MACROS
|
||||
#define __STDC_LIMIT_MACROS
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <error.h>
|
||||
#include <fcntl.h>
|
||||
#include <libgen.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
DIR* fdopendupdir(int fd)
|
||||
{
|
||||
int newfd = dup(fd);
|
||||
if ( newfd < 0 )
|
||||
return NULL;
|
||||
DIR* result = fdopendir(newfd);
|
||||
if ( !result )
|
||||
return close(newfd), (DIR*) NULL;
|
||||
return result;
|
||||
}
|
||||
|
||||
int ftestexecutableat(int dirfd, const char* path)
|
||||
{
|
||||
struct stat st;
|
||||
if ( fstatat(dirfd, path, &st, AT_SYMLINK_NOFOLLOW) != 0 )
|
||||
return -1;
|
||||
if ( !S_ISREG(st.st_mode) )
|
||||
return errno = EISDIR, -1;
|
||||
if ( faccessat(dirfd, path, X_OK, AT_EACCESS | AT_SYMLINK_NOFOLLOW) == 0 )
|
||||
return 1;
|
||||
else if ( errno == EACCES )
|
||||
return 0;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
void execdiff(int tree_a, const char* tree_a_path,
|
||||
int tree_b, const char* tree_b_path,
|
||||
const char* relpath)
|
||||
{
|
||||
DIR* dir_b = fdopendupdir(tree_b);
|
||||
if ( !dir_b )
|
||||
error(1, errno, "fdopendupdir(`%s`)", tree_b_path);
|
||||
while ( struct dirent* entry = readdir(dir_b) )
|
||||
{
|
||||
if ( !strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..") )
|
||||
continue;
|
||||
|
||||
char* subrelpath = join_paths(relpath, entry->d_name);
|
||||
|
||||
int diropenflags = O_RDONLY | O_DIRECTORY | O_NOFOLLOW;
|
||||
int subtree_b = openat(tree_b, entry->d_name, diropenflags);
|
||||
if ( 0 <= subtree_b )
|
||||
{
|
||||
char* subtree_b_path = join_paths(tree_b_path, entry->d_name);
|
||||
int subtree_a = openat(tree_a, entry->d_name, diropenflags);
|
||||
if ( subtree_a < 0 )
|
||||
{
|
||||
if ( !(errno == ENOTDIR || errno == ELOOP || errno == ENOENT) )
|
||||
error(1, errno, "`%s/%s`", tree_b_path, entry->d_name);
|
||||
execdiff(-1, NULL, subtree_b, subtree_b_path, subrelpath);
|
||||
free(subtree_b_path);
|
||||
close(subtree_b);
|
||||
free(subrelpath);
|
||||
continue;
|
||||
}
|
||||
char* subtree_a_path = join_paths(tree_a_path, entry->d_name);
|
||||
execdiff(subtree_a, subtree_a_path, subtree_b, subtree_b_path,
|
||||
subrelpath);
|
||||
free(subtree_a_path);
|
||||
close(subtree_a);
|
||||
free(subtree_b_path);
|
||||
close(subtree_b);
|
||||
free(subrelpath);
|
||||
continue;
|
||||
}
|
||||
else if ( !(errno == ENOTDIR || errno == ELOOP) )
|
||||
error(1, errno, "`%s/%s`", tree_b_path, entry->d_name);
|
||||
|
||||
int a_executableness = ftestexecutableat(tree_a, entry->d_name);
|
||||
int b_executableness = ftestexecutableat(tree_b, entry->d_name);
|
||||
|
||||
if ( (a_executableness == 1) && (b_executableness == 0) )
|
||||
{
|
||||
printf("chmod -x -- '");
|
||||
for ( size_t i = 0; subrelpath[i]; i++ )
|
||||
if ( subrelpath[i] == '\'' )
|
||||
printf("'\\''");
|
||||
else
|
||||
putchar(subrelpath[i]);
|
||||
printf("'\n");
|
||||
}
|
||||
|
||||
if ( (a_executableness != 1) && (b_executableness == 1) )
|
||||
{
|
||||
printf("chmod +x -- '");
|
||||
for ( size_t i = 0; subrelpath[i]; i++ )
|
||||
if ( subrelpath[i] == '\'' )
|
||||
printf("'\\''");
|
||||
else
|
||||
putchar(subrelpath[i]);
|
||||
printf("'\n");
|
||||
}
|
||||
|
||||
free(subrelpath);
|
||||
continue;
|
||||
}
|
||||
closedir(dir_b);
|
||||
}
|
||||
|
||||
void help(FILE* fp, const char* argv0)
|
||||
{
|
||||
fprintf(fp, "Usage: %s [OPTION]... TREE-A TREE-B\n", argv0);
|
||||
fprintf(fp, "Reports which files have had the executable bit changed between two trees.\n");
|
||||
}
|
||||
|
||||
void version(FILE* fp, const char* argv0)
|
||||
{
|
||||
help(fp, argv0);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
const char* argv0 = argv[0];
|
||||
for ( int i = 0; i < argc; i++ )
|
||||
{
|
||||
const char* arg = argv[i];
|
||||
if ( arg[0] != '-' )
|
||||
continue;
|
||||
argv[i] = NULL;
|
||||
if ( !strcmp(arg, "--") )
|
||||
break;
|
||||
if ( arg[1] != '-' )
|
||||
{
|
||||
while ( char c = *++arg ) switch ( c )
|
||||
{
|
||||
default:
|
||||
fprintf(stderr, "%s: unknown option -- `%c'\n", argv0, c);
|
||||
help(stderr, argv0);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
else if ( !strcmp(arg, "--help") ) { help(stdout, argv0); exit(0); }
|
||||
else if ( !strcmp(arg, "--version") ) { version(stdout, argv0); exit(0); }
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "%s: unknown option: `%s'\n", argv0, arg);
|
||||
help(stderr, argv0);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if ( argc == 1 )
|
||||
{
|
||||
help(stdout, argv0);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
CompactArguments(&argc, &argv);
|
||||
|
||||
if ( argc < 3 )
|
||||
{
|
||||
fprintf(stderr, "%s: you need to specify two directories\n", argv0);
|
||||
help(stderr, argv0);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if ( 3 < argc )
|
||||
{
|
||||
fprintf(stderr, "%s: unexpected extra operand `%s'\n", argv0, argv[2]);
|
||||
help(stderr, argv0);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
const char* tree_a_path = argv[1];
|
||||
int tree_a = open(tree_a_path, O_RDONLY | O_DIRECTORY);
|
||||
if ( tree_a < 0 )
|
||||
error(1, errno, "`%s'", tree_a_path);
|
||||
|
||||
const char* tree_b_path = argv[2];
|
||||
int tree_b = open(tree_b_path, O_RDONLY | O_DIRECTORY);
|
||||
if ( tree_b < 0 )
|
||||
error(1, errno, "`%s'", tree_b_path);
|
||||
|
||||
execdiff(tree_a, tree_a_path, tree_b, tree_b_path, ".");
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,246 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2013.
|
||||
|
||||
This file is part of Tix.
|
||||
|
||||
Tix is free software: you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
Tix is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Tix. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
tix-execpatch.cpp
|
||||
Patches the executable bits of files in the current source directory.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#define __STDC_CONSTANT_MACROS
|
||||
#define __STDC_LIMIT_MACROS
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <error.h>
|
||||
#include <fcntl.h>
|
||||
#include <libgen.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
int fgetc_or_die(FILE* input, const char* input_path, size_t* line,
|
||||
size_t* column)
|
||||
{
|
||||
int result = fgetc(input);
|
||||
if ( result == '\n' )
|
||||
(*line)++, *column = 0;
|
||||
else
|
||||
(*column)++;
|
||||
if ( result == EOF && ferror(input) )
|
||||
error(1, errno, "read: `%s'", input_path);
|
||||
return result;
|
||||
}
|
||||
|
||||
int fgetc_or_die_eof(FILE* input, const char* input_path, size_t* line,
|
||||
size_t* column)
|
||||
{
|
||||
int result = fgetc_or_die(input, input_path, line, column);
|
||||
if ( result == EOF )
|
||||
error(1, errno, "%s:%zu:%zu: unexpected end of file",
|
||||
input_path, *line, *column);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool check_eof(FILE* input, const char* input_path)
|
||||
{
|
||||
size_t line = 0;
|
||||
size_t column = 0;
|
||||
int c = fgetc_or_die(input, input_path, &line, &column);
|
||||
if ( c != EOF )
|
||||
{
|
||||
ungetc(c, input);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void parse_fixed(const char* text, FILE* input, const char* input_path,
|
||||
size_t* line, size_t* column)
|
||||
{
|
||||
for ( size_t i = 0; text[i]; i++ )
|
||||
{
|
||||
int ic = fgetc_or_die(input, input_path, line, column);
|
||||
if ( ic == EOF )
|
||||
error(1, errno, "%s:%zu:%zu: unexpected end of file, expected `%s'",
|
||||
input_path, *line, *column, text + i);
|
||||
if ( ic != (unsigned char) text[i] )
|
||||
error(1, errno, "%s:%zu:%zu: parse error, expected `%s'", input_path,
|
||||
*line, *column, text + i);
|
||||
}
|
||||
}
|
||||
|
||||
bool execpatch(FILE* input, const char* input_path, bool check)
|
||||
{
|
||||
char* buffer = NULL;
|
||||
size_t buffer_used = 0;
|
||||
size_t buffer_length = 0;
|
||||
|
||||
bool result = true;
|
||||
size_t line = 1;
|
||||
while ( !check_eof(input, input_path) )
|
||||
{
|
||||
buffer_used = 0;
|
||||
size_t column = 0;
|
||||
parse_fixed("chmod ", input, input_path, &line, &column);
|
||||
bool plus;
|
||||
switch ( fgetc_or_die(input, input_path, &line, &column) )
|
||||
{
|
||||
case '-': plus = false; break;
|
||||
case '+': plus = true; break;
|
||||
default:
|
||||
error(1, errno, "%s:%zu:%zu: parse error, expected '-' or '+'",
|
||||
input_path, line, column);
|
||||
}
|
||||
parse_fixed("x -- '", input, input_path, &line, &column);
|
||||
while ( true )
|
||||
{
|
||||
int ic = fgetc_or_die_eof(input, input_path, &line, &column);
|
||||
if ( ic == '\'' )
|
||||
{
|
||||
ic = fgetc_or_die(input, input_path, &line, &column);
|
||||
if ( ic == EOF || ic == '\n' )
|
||||
break;
|
||||
ungetc(ic, input);
|
||||
parse_fixed("\\''", input, input_path, &line, &column);
|
||||
ic = '\'';
|
||||
}
|
||||
if ( buffer_used == buffer_length )
|
||||
{
|
||||
size_t new_length = buffer_length ? 2 * buffer_length : 16;
|
||||
buffer = (char*) realloc(buffer, sizeof(char) * (new_length + 1));
|
||||
buffer_length = new_length;
|
||||
}
|
||||
buffer[buffer_used++] = ic;
|
||||
}
|
||||
if ( !buffer_used )
|
||||
error(1, errno, "%s:%zu: unexpected empty path", input_path, line);
|
||||
assert(buffer_length);
|
||||
buffer[buffer_used] = '\0';
|
||||
if ( buffer[0] == '/' )
|
||||
error(1, errno, "%s:%zu: unexpected absolute path", input_path, line);
|
||||
if ( does_path_contain_dotdot(buffer) )
|
||||
error(1, errno, "%s:%zu: unexpected path with ..", input_path, line);
|
||||
if ( check )
|
||||
continue;
|
||||
struct stat st;
|
||||
if ( fstatat(AT_FDCWD, buffer, &st, AT_SYMLINK_NOFOLLOW) != 0 )
|
||||
{
|
||||
error(0, errno, "chmod %cx: `%s'", plus ? '+' : '-', buffer);
|
||||
result = false;
|
||||
continue;
|
||||
}
|
||||
mode_t new_mode = st.st_mode;
|
||||
if ( plus )
|
||||
new_mode |= 0111 & ~get_umask_value();
|
||||
else
|
||||
new_mode &= ~0111;
|
||||
if ( fchmodat(AT_FDCWD, buffer, new_mode, 0) != 0 )
|
||||
{
|
||||
error(0, errno, "chmod %cx: `%s'", plus ? '+' : '-', buffer);
|
||||
result = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void help(FILE* fp, const char* argv0)
|
||||
{
|
||||
fprintf(fp, "Usage: %s [OPTION]... [PATCH]\n", argv0);
|
||||
fprintf(fp, "Patches the executable bits of files in the current source directory.\n");
|
||||
}
|
||||
|
||||
void version(FILE* fp, const char* argv0)
|
||||
{
|
||||
help(fp, argv0);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
bool check = false;
|
||||
char* directory = NULL;
|
||||
|
||||
const char* argv0 = argv[0];
|
||||
for ( int i = 0; i < argc; i++ )
|
||||
{
|
||||
const char* arg = argv[i];
|
||||
if ( arg[0] != '-' )
|
||||
continue;
|
||||
argv[i] = NULL;
|
||||
if ( !strcmp(arg, "--") )
|
||||
break;
|
||||
if ( arg[1] != '-' )
|
||||
{
|
||||
while ( char c = *++arg ) switch ( c )
|
||||
{
|
||||
case 'c': check = true; break;
|
||||
default:
|
||||
fprintf(stderr, "%s: unknown option -- `%c'\n", argv0, c);
|
||||
help(stderr, argv0);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
else if ( !strcmp(arg, "--help") ) { help(stdout, argv0); exit(0); }
|
||||
else if ( !strcmp(arg, "--version") ) { version(stdout, argv0); exit(0); }
|
||||
else if ( !strcmp(arg, "--check") ) { check = true; }
|
||||
else if ( GET_OPTION_VARIABLE("--directory", &directory) ) { }
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "%s: unknown option: `%s'\n", argv0, arg);
|
||||
help(stderr, argv0);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
CompactArguments(&argc, &argv);
|
||||
|
||||
if ( 2 < argc )
|
||||
{
|
||||
fprintf(stderr, "%s: unexpected extra operand `%s'\n", argv0, argv[2]);
|
||||
help(stderr, argv0);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
const char* input_path = "<stdin>";
|
||||
FILE* input = stdin;
|
||||
|
||||
if ( argc == 2 )
|
||||
{
|
||||
input_path = argv[1];
|
||||
if ( !(input = fopen(input_path, "r")) )
|
||||
error(1, errno, "`%s'", input_path);
|
||||
}
|
||||
|
||||
if ( directory && chdir(directory) != 0 )
|
||||
error(1, errno, "chdir: `%s'", directory);
|
||||
free(directory);
|
||||
|
||||
return execpatch(input, input_path, check) ? 0 : 1;
|
||||
}
|
|
@ -0,0 +1,342 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2013.
|
||||
|
||||
This file is part of Tix.
|
||||
|
||||
Tix is free software: you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
Tix is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Tix. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
tix-install.cpp
|
||||
Install a tix into a tix collection.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#define __STDC_CONSTANT_MACROS
|
||||
#define __STDC_LIMIT_MACROS
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <error.h>
|
||||
#include <errno.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
void TipTixCollection(const char* prefix)
|
||||
{
|
||||
error(0, 0, "error: `%s' isn't a tix collection, use tix-collection before "
|
||||
"installing packages.", prefix);
|
||||
}
|
||||
|
||||
void TipTixWrongPlatform(const char* prefix, const char* platform)
|
||||
{
|
||||
error(0, 0, "error: `%s' isn't a tix collection for platform `%s', use "
|
||||
"tix-collection to add this platform before installing "
|
||||
"packages.", prefix, platform);
|
||||
}
|
||||
|
||||
void VerifyTixCollection(const char* prefix)
|
||||
{
|
||||
if ( !IsDirectory(prefix) )
|
||||
{
|
||||
if ( errno == ENOENT )
|
||||
TipTixCollection(prefix);
|
||||
error(1, errno, "error: tix collection unavailable: `%s'", prefix);
|
||||
}
|
||||
}
|
||||
|
||||
void VerifyTixDirectory(const char* prefix, const char* tix_dir)
|
||||
{
|
||||
if ( !IsDirectory(tix_dir) )
|
||||
{
|
||||
if ( errno == ENOENT )
|
||||
TipTixCollection(prefix);
|
||||
error(1, errno, "error: tix database unavailable: `%s'", tix_dir);
|
||||
}
|
||||
}
|
||||
|
||||
void VerifyTixDatabase(const char* prefix,
|
||||
const char* tixdb_path,
|
||||
const char* platform)
|
||||
{
|
||||
if ( !IsDirectory(tixdb_path) )
|
||||
{
|
||||
if ( errno == ENOENT )
|
||||
TipTixWrongPlatform(prefix, platform);
|
||||
error(1, errno, "error: tix database for platform `%s' unavailable: "
|
||||
"`%s'", platform, tixdb_path);
|
||||
}
|
||||
char* info_path = join_paths(tixdb_path, "collection.conf");
|
||||
if ( !IsFile(info_path) )
|
||||
{
|
||||
if ( errno == ENOENT )
|
||||
TipTixCollection(prefix);
|
||||
error(1, errno, "error: tix collection information unavailable: `%s'",
|
||||
info_path);
|
||||
}
|
||||
char* installed_list_path = join_paths(tixdb_path, "installed.list");
|
||||
FILE* installed_list_fp = fopen(installed_list_path, "a");
|
||||
if ( !installed_list_fp )
|
||||
{
|
||||
error(0, errno, "error: unable to open `%s' for writing",
|
||||
installed_list_path);
|
||||
error(1, 0, "error: `%s': do you have sufficient permissions to "
|
||||
"administer this tix collection?", prefix);
|
||||
}
|
||||
fclose(installed_list_fp);
|
||||
free(installed_list_path);
|
||||
free(info_path);
|
||||
}
|
||||
|
||||
bool IsPackageInstalled(const char* tixdb_path, const char* package)
|
||||
{
|
||||
char* installed_list_path = join_paths(tixdb_path, "installed.list");
|
||||
FILE* installed_list_fp = fopen(installed_list_path, "r");
|
||||
if ( !installed_list_fp )
|
||||
error(1, errno, "`%s'", installed_list_path);
|
||||
|
||||
bool ret = false;
|
||||
char* line = NULL;
|
||||
size_t line_size;
|
||||
ssize_t line_len;
|
||||
while ( 0 < (line_len = getline(&line, &line_size, installed_list_fp)) )
|
||||
{
|
||||
if ( line_len && line[line_len-1] == '\n' )
|
||||
line[--line_len] = '\0';
|
||||
if ( !strcmp(line, package) )
|
||||
{
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(line);
|
||||
|
||||
fclose(installed_list_fp);
|
||||
free(installed_list_path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void MarkPackageAsInstalled(const char* tixdb_path, const char* package)
|
||||
{
|
||||
char* installed_list_path = join_paths(tixdb_path, "installed.list");
|
||||
FILE* installed_list_fp = fopen(installed_list_path, "a");
|
||||
if ( !installed_list_fp )
|
||||
error(1, errno, "`%s'", installed_list_path);
|
||||
|
||||
fprintf(installed_list_fp, "%s\n", package);
|
||||
fflush(installed_list_fp);
|
||||
|
||||
if ( ferror(installed_list_fp) || fclose(installed_list_fp) == EOF )
|
||||
error(1, errno, "`%s'", installed_list_path);
|
||||
free(installed_list_path);
|
||||
}
|
||||
|
||||
void Usage(FILE* fp, const char* argv0)
|
||||
{
|
||||
fprintf(fp, "Usage: %s [OPTION]... --collection=PREFIX PACKAGE\n", argv0);
|
||||
fprintf(fp, "Install a tix into a tix collection.\n");
|
||||
}
|
||||
|
||||
void Help(FILE* fp, const char* argv0)
|
||||
{
|
||||
Usage(fp, argv0);
|
||||
}
|
||||
|
||||
void Version(FILE* fp, const char* argv0)
|
||||
{
|
||||
Usage(fp, argv0);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
char* collection = strdup_null(getenv_def("TIX_COLLECTION", NULL));
|
||||
char* prefix = strdup_null(getenv_def("TIX_COLLECTION_PREFIX", NULL));
|
||||
char* tar = strdup(getenv_def("TAR", "tar"));
|
||||
bool reinstall = false;
|
||||
|
||||
const char* argv0 = argv[0];
|
||||
for ( int i = 0; i < argc; i++ )
|
||||
{
|
||||
const char* arg = argv[i];
|
||||
if ( arg[0] != '-' )
|
||||
continue;
|
||||
argv[i] = NULL;
|
||||
if ( !strcmp(arg, "--") )
|
||||
break;
|
||||
if ( arg[1] != '-' )
|
||||
{
|
||||
while ( char c = *++arg ) switch ( c )
|
||||
{
|
||||
default:
|
||||
fprintf(stderr, "%s: unknown option -- `%c'\n", argv0, c);
|
||||
Usage(stderr, argv0);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
else if ( !strcmp(arg, "--help") ) { Help(stdout, argv0); exit(0); }
|
||||
else if ( !strcmp(arg, "--usage") ) { Usage(stdout, argv0); exit(0); }
|
||||
else if ( !strcmp(arg, "--version") ) { Version(stdout, argv0); exit(0); }
|
||||
else if ( GET_OPTION_VARIABLE("--collection", &collection) ) { }
|
||||
else if ( GET_OPTION_VARIABLE("--prefix", &prefix) ) { }
|
||||
else if ( GET_OPTION_VARIABLE("--tar", &tar) ) { }
|
||||
else if ( !strcmp(arg, "--reinstall") ) { reinstall = true; }
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "%s: unknown option: `%s'\n", argv0, arg);
|
||||
Usage(stderr, argv0);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if ( argc == 1 )
|
||||
{
|
||||
Usage(stdout, argv0);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
CompactArguments(&argc, &argv);
|
||||
|
||||
if ( argc <= 1 )
|
||||
{
|
||||
fprintf(stderr, "%s: no package specified\n", argv0);
|
||||
Usage(stderr, argv0);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if ( !collection && prefix )
|
||||
collection = strdup(prefix);
|
||||
|
||||
if ( !collection )
|
||||
{
|
||||
fprintf(stderr, "%s: no collection prefix specified, use --collection "
|
||||
"or TIX_COLLECTION to specify where the package will "
|
||||
"installed.\n", argv0);
|
||||
Usage(stderr, argv0);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if ( !prefix )
|
||||
prefix = strdup(collection);
|
||||
|
||||
if ( strcmp(collection, prefix) != 0 )
|
||||
error(1, 0, "error: desired collection `%s' isn't equal to desired "
|
||||
"prefix `%s', which isn't supported (and dangerous).\n",
|
||||
collection, prefix);
|
||||
|
||||
if ( !*collection )
|
||||
collection = strdup("/");
|
||||
|
||||
VerifyTixCollection(collection);
|
||||
|
||||
char* tix_directory_path = join_paths(collection, "tix");
|
||||
|
||||
VerifyTixDirectory(collection, tix_directory_path);
|
||||
|
||||
char* tix_path = strdup(argv[1]);
|
||||
if ( !IsFile(tix_path) )
|
||||
error(1, errno, "`%s'", tix_path);
|
||||
|
||||
const char* tixinfo_path = "tix/tixinfo";
|
||||
if ( !TarContainsFile(tar, tix_path, tixinfo_path) )
|
||||
error(1, 0, "`%s' doesn't contain a `%s' file", tix_path, tixinfo_path);
|
||||
|
||||
string_array_t tixinfo = string_array_make();
|
||||
FILE* tixinfo_fp = TarOpenFile(tar, tix_path, tixinfo_path);
|
||||
dictionary_append_file(&tixinfo, tixinfo_fp);
|
||||
fclose(tixinfo_fp);
|
||||
|
||||
const char* package_name = dictionary_get(&tixinfo, "pkg.name");
|
||||
assert(package_name);
|
||||
|
||||
const char* package_prefix = dictionary_get(&tixinfo, "pkg.prefix");
|
||||
|
||||
const char* package_platform = dictionary_get(&tixinfo, "tix.platform");
|
||||
assert(package_platform);
|
||||
|
||||
char* tixdb_path = join_paths(tix_directory_path, package_platform);
|
||||
free(tix_directory_path);
|
||||
|
||||
VerifyTixDatabase(collection, tixdb_path, package_platform);
|
||||
|
||||
char* coll_conf_path = join_paths(tixdb_path, "collection.conf");
|
||||
string_array_t coll_conf = string_array_make();
|
||||
if ( !dictionary_append_file_path(&coll_conf, coll_conf_path) )
|
||||
error(1, errno, "`%s'", coll_conf_path);
|
||||
VerifyTixCollectionConfiguration(&coll_conf, coll_conf_path);
|
||||
free(coll_conf_path);
|
||||
|
||||
const char* coll_prefix = dictionary_get(&coll_conf, "collection.prefix");
|
||||
assert(coll_prefix);
|
||||
const char* coll_platform = dictionary_get(&coll_conf, "collection.platform");
|
||||
assert(coll_platform);
|
||||
|
||||
bool already_installed = IsPackageInstalled(tixdb_path, package_name);
|
||||
if ( already_installed && !reinstall )
|
||||
error(1, 0, "error: package `%s' is already installed.", package_name);
|
||||
|
||||
if ( package_prefix && strcmp(coll_prefix, package_prefix) != 0 )
|
||||
{
|
||||
error(0, errno, "error: `%s' is compiled with the prefix `%s', but the "
|
||||
"destination collection has the prefix `%s'.", tix_path,
|
||||
package_prefix, coll_prefix);
|
||||
error(1, errno, "you need to recompile the package with "
|
||||
"--prefix=\"%s\".", coll_prefix);
|
||||
}
|
||||
|
||||
if ( strcmp(coll_platform, package_platform) != 0 )
|
||||
{
|
||||
error(0, errno, "error: `%s' is compiled with the platform `%s', but "
|
||||
"the destination collection has the platform `%s'.",
|
||||
tix_path, package_platform, coll_platform);
|
||||
error(1, errno, "you need to recompile the package with "
|
||||
"--host=%s\".", coll_platform);
|
||||
}
|
||||
|
||||
printf("Installing `%s' into `%s'...\n", package_name, prefix);
|
||||
char* data_and_prefix = package_prefix && prefix[0] ?
|
||||
print_string("data%s", package_prefix) :
|
||||
strdup("data");
|
||||
if ( fork_and_wait_or_death() )
|
||||
{
|
||||
size_t num_strips = count_tar_components(data_and_prefix);
|
||||
const char* cmd_argv[] =
|
||||
{
|
||||
tar,
|
||||
print_string("--strip-components=%zu", num_strips),
|
||||
"-C", prefix,
|
||||
"--extract",
|
||||
"--file", tix_path,
|
||||
data_and_prefix,
|
||||
NULL
|
||||
};
|
||||
execvp(cmd_argv[0], (char* const*) cmd_argv);
|
||||
error(127, errno, "%s", cmd_argv[0]);
|
||||
}
|
||||
free(data_and_prefix);
|
||||
|
||||
if ( !already_installed )
|
||||
MarkPackageAsInstalled(tixdb_path, package_name);
|
||||
}
|
|
@ -0,0 +1,427 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2013.
|
||||
|
||||
This file is part of Tix.
|
||||
|
||||
Tix is free software: you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
Tix is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Tix. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
tix-object-insert.cpp
|
||||
Inserts files into a tix object database.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <error.h>
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <nettle/sha.h>
|
||||
|
||||
int create_and_open_directory_at(int dirfd, const char* path, mode_t mode)
|
||||
{
|
||||
|
||||
int ret = openat(dirfd, path, O_RDONLY | O_DIRECTORY);
|
||||
if ( ret < 0 && errno == EEXIST )
|
||||
{
|
||||
if ( mkdirat(dirfd, path, mode) != 0 )
|
||||
return -1;
|
||||
return openat(dirfd, path, O_RDONLY | O_DIRECTORY);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct options
|
||||
{
|
||||
bool hash;
|
||||
bool quiet;
|
||||
bool link;
|
||||
bool symlink;
|
||||
};
|
||||
|
||||
bool insert_object(struct options* opts,
|
||||
const char* file_path,
|
||||
const char* tmp,
|
||||
int database_fd, const char* database_path,
|
||||
const char* digest,
|
||||
int dir_fd, const char* dir_name)
|
||||
{
|
||||
const char* entry_name = digest + 2;
|
||||
if ( linkat(database_fd, tmp, dir_fd, entry_name, 0) &&
|
||||
errno != EEXIST )
|
||||
{
|
||||
error(0, errno, "`%s/%s' -> `%s/%s/%s'", database_path, dir_name,
|
||||
database_path, dir_name, entry_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( opts->hash )
|
||||
printf("%s\n", digest);
|
||||
else if ( !opts->quiet )
|
||||
printf("`%s' -> `%s/%s/%s'\n", file_path,
|
||||
database_path, dir_name, entry_name);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool insert_object(struct options* opts,
|
||||
const char* file_path,
|
||||
const char* tmp,
|
||||
int database_fd, const char* database_path,
|
||||
const char* digest)
|
||||
{
|
||||
char dir_name[3] = { digest[0], digest[1], '\0' };
|
||||
int dir_fd = create_and_open_directory_at(database_fd, dir_name, 0777);
|
||||
if ( dir_fd < 0 )
|
||||
{
|
||||
error(0, errno, "`%s/%s'", database_path, dir_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = insert_object(opts, file_path, tmp, database_fd, database_path, digest, dir_fd, dir_name);
|
||||
|
||||
close(dir_fd);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool copy_and_hash(FILE* fpin, const char* file_path,
|
||||
FILE* fpout, const char* tmp,
|
||||
int /*database_fd*/, const char* database_path,
|
||||
char* digest)
|
||||
{
|
||||
struct sha256_ctx shactx;
|
||||
sha256_init(&shactx);
|
||||
|
||||
uint8_t buffer[SHA256_DATA_SIZE];
|
||||
while ( size_t num_bytes = fread(buffer, 1, sizeof(buffer), fpin) )
|
||||
{
|
||||
assert(num_bytes <= UINT_MAX);
|
||||
sha256_update(&shactx, num_bytes, buffer);
|
||||
if ( fwrite(buffer, 1, num_bytes, fpout) != num_bytes )
|
||||
{
|
||||
error(0, errno, "`%s/%s'", database_path, tmp);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ferror(fpin) )
|
||||
{
|
||||
error(0, errno, "`%s'", file_path);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( fflush(fpout) != 0 )
|
||||
{
|
||||
error(0, errno, "`%s/%s'", database_path, tmp);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t binary_digest[SHA256_DIGEST_SIZE];
|
||||
sha256_digest(&shactx, sizeof(binary_digest), binary_digest);
|
||||
|
||||
for ( size_t n = 0; n < SHA256_DIGEST_SIZE; n++ )
|
||||
snprintf(digest + 2 * n, 3, "%02x", binary_digest[n]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool insert_object(struct options* opts,
|
||||
FILE* fpin, const char* file_path,
|
||||
FILE* fpout, const char* tmp,
|
||||
int database_fd, const char* database_path)
|
||||
{
|
||||
char digest[2 * SHA256_DIGEST_SIZE + 1];
|
||||
|
||||
if ( !copy_and_hash(fpin, file_path, fpout, tmp, database_fd,
|
||||
database_path, digest) )
|
||||
return false;
|
||||
|
||||
return insert_object(opts, file_path, tmp, database_fd, database_path, digest);
|
||||
}
|
||||
|
||||
// TODO: Preferably use O_TMPFILE to avoid naming the file until we have made a
|
||||
// copy whose hash we know.
|
||||
bool insert_object(struct options* opts,
|
||||
FILE* fpin, const char* file_path,
|
||||
int database_fd, const char* database_path)
|
||||
{
|
||||
char tmp[8 + 1 + sizeof(pid_t) * 3 + 1];
|
||||
snprintf(tmp, sizeof(tmp), "incoming.%ji", (intmax_t) getpid());
|
||||
int tmp_fd = openat(database_fd, tmp, O_CREAT | O_WRONLY | O_EXCL, 0444);
|
||||
if ( tmp_fd < 0 )
|
||||
{
|
||||
error(0, errno, "`%s/%s'", database_path, tmp);
|
||||
return false;
|
||||
}
|
||||
|
||||
FILE* fpout = fdopen(tmp_fd, "w");
|
||||
if ( !fpout )
|
||||
{
|
||||
error(0, errno, "fdopen(%i)", tmp_fd);
|
||||
close(tmp_fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = insert_object(opts, fpin, file_path, fpout, tmp, database_fd, database_path);
|
||||
|
||||
fclose(fpout);
|
||||
|
||||
unlinkat(database_fd, tmp, 0);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool insert_object(struct options* opts,
|
||||
const char* file_path,
|
||||
int database_fd, const char* database_path)
|
||||
{
|
||||
if ( !strcmp(file_path, "-") )
|
||||
return insert_object(opts, stdin, file_path, database_fd, database_path);
|
||||
|
||||
FILE* fpin = fopen(file_path, "r");
|
||||
if ( !fpin )
|
||||
error(1, errno, "`%s'", file_path);
|
||||
|
||||
bool success = insert_object(opts, fpin, file_path, database_fd, database_path);
|
||||
|
||||
fclose(fpin);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void help(FILE* fp, const char* argv0)
|
||||
{
|
||||
fprintf(fp, "Usage: %s [OPTION]... [-l | -s] --database DATABASE FILE...\n", argv0);
|
||||
fprintf(fp, "Inserts files into a tix object database.\n");
|
||||
}
|
||||
|
||||
void usage(FILE* fp, const char* argv0)
|
||||
{
|
||||
help(fp, argv0);
|
||||
}
|
||||
|
||||
void version(FILE* fp, const char* argv0)
|
||||
{
|
||||
help(fp, argv0);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
const char* argv0 = argv[0];
|
||||
const char* database_path = NULL;
|
||||
bool hash = false;
|
||||
bool quiet = false;
|
||||
bool do_link = false;
|
||||
bool do_symlink = false;
|
||||
|
||||
for ( int i = 0; i < argc; i++ )
|
||||
{
|
||||
const char* arg = argv[i];
|
||||
if ( arg[0] != '-' )
|
||||
continue;
|
||||
argv[i] = NULL;
|
||||
if ( !strcmp(arg, "--") )
|
||||
break;
|
||||
if ( arg[1] != '-' )
|
||||
{
|
||||
while ( char c = *++arg ) switch ( c )
|
||||
{
|
||||
case 'h': hash = true;
|
||||
case 'l': do_link = true;
|
||||
case 'q': quiet = true;
|
||||
case 's': do_link = true;
|
||||
default:
|
||||
fprintf(stderr, "%s: unknown option -- '%c'\n", argv0, c);
|
||||
usage(stderr, argv0);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
else if ( !strcmp(arg, "--help") )
|
||||
help(stdout, argv0), exit(0);
|
||||
else if ( !strcmp(arg, "--usage") )
|
||||
usage(stdout, argv0), exit(0);
|
||||
else if ( !strcmp(arg, "--version") )
|
||||
version(stdout, argv0), exit(0);
|
||||
else if ( !strcmp(arg, "--database") )
|
||||
{
|
||||
if ( i + 1 == argc )
|
||||
error(1, 0, "`--database' expected argument");
|
||||
database_path = argv[++i], argv[i] = NULL;
|
||||
}
|
||||
else if ( !strcmp(arg, "--hash") )
|
||||
hash = true;
|
||||
else if ( !strcmp(arg, "--link") )
|
||||
do_link = true;
|
||||
else if ( !strcmp(arg, "--quiet") )
|
||||
quiet = true;
|
||||
else if ( !strcmp(arg, "--symlink") )
|
||||
do_symlink = true;
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "%s: unknown option: `%s'\n", argv0, arg);
|
||||
usage(stderr, argv0);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if ( argc == 1 )
|
||||
usage(stdout, argv0), exit(0);
|
||||
|
||||
if ( do_link && do_symlink )
|
||||
{
|
||||
error(0, 0, "error: the -l and -s options are mutually exclusive");
|
||||
usage(stdout, argv0);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if ( !database_path )
|
||||
{
|
||||
error(1, 0, "no `--database' option given, don't know what database");
|
||||
usage(stdout, argv0);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int database_fd = open(database_path, O_RDONLY | O_DIRECTORY);
|
||||
if ( database_fd < 0 )
|
||||
error(1, errno, "`%s'", database_path);
|
||||
|
||||
for ( int i = 1; i < argc; i++ )
|
||||
{
|
||||
const char* file_path = argv[i];
|
||||
if ( !file_path )
|
||||
continue;
|
||||
|
||||
FILE* fpin = fopen(file_path, "r");
|
||||
if ( !fpin )
|
||||
error(1, errno, "`%s'", file_path);
|
||||
|
||||
char tmp[8 + 1 + sizeof(pid_t) * 3 + 1];
|
||||
snprintf(tmp, sizeof(tmp), "incoming.%ju", (uintmax_t) getpid());
|
||||
int tmp_fd = openat(database_fd, tmp, O_CREAT | O_WRONLY | O_EXCL, 0444);
|
||||
if ( tmp_fd < 0 )
|
||||
error(1, errno, "`%s/%s'", database_path, tmp);
|
||||
FILE* fpout = fdopen(tmp_fd, "w");
|
||||
|
||||
struct sha256_ctx shactx;
|
||||
sha256_init(&shactx);
|
||||
|
||||
uint8_t buffer[SHA256_DATA_SIZE];
|
||||
while ( size_t num_bytes = fread(buffer, 1, sizeof(buffer), fpin) )
|
||||
{
|
||||
assert(num_bytes <= UINT_MAX);
|
||||
sha256_update(&shactx, num_bytes, buffer);
|
||||
if ( fwrite(buffer, 1, num_bytes, fpout) != num_bytes )
|
||||
{
|
||||
unlinkat(database_fd, tmp, 0);
|
||||
error(1, errno, "`%s/%s'", database_path, tmp);
|
||||
}
|
||||
}
|
||||
|
||||
if ( ferror(fpin) )
|
||||
{
|
||||
unlinkat(database_fd, tmp, 0);
|
||||
error(1, errno, "`%s'", file_path);
|
||||
}
|
||||
fclose(fpin);
|
||||
|
||||
if ( fflush(fpout) != 0 )
|
||||
{
|
||||
unlinkat(database_fd, tmp, 0);
|
||||
error(1, errno, "`%s/%s'", database_path, tmp);
|
||||
}
|
||||
|
||||
fclose(fpout);
|
||||
|
||||
uint8_t binary_digest[SHA256_DIGEST_SIZE];
|
||||
sha256_digest(&shactx, sizeof(binary_digest), binary_digest);
|
||||
char digest[2 * SHA256_DIGEST_SIZE + 1];
|
||||
for ( size_t n = 0; n < SHA256_DIGEST_SIZE; n++ )
|
||||
snprintf(digest + 2 * n, 3, "%02x", binary_digest[n]);
|
||||
|
||||
char dir_name[3] = { digest[0], digest[1], '\0' };
|
||||
if ( mkdirat(database_fd, dir_name, 0777) != 0 &&
|
||||
errno != EEXIST )
|
||||
{
|
||||
unlinkat(database_fd, tmp, 0);
|
||||
error(1, errno, "`%s/%s'", database_path, dir_name);
|
||||
}
|
||||
|
||||
int dir_fd = openat(database_fd, dir_name, O_RDONLY | O_DIRECTORY);
|
||||
if ( dir_fd < 0 )
|
||||
{
|
||||
unlinkat(database_fd, tmp, 0);
|
||||
error(1, errno, "`%s/%s'", database_path, dir_name);
|
||||
}
|
||||
|
||||
const char* entry_name = digest + 2;
|
||||
if ( linkat(database_fd, tmp, dir_fd, entry_name, 0) &&
|
||||
errno != EEXIST )
|
||||
{
|
||||
unlinkat(database_fd, tmp, 0);
|
||||
error(1, errno, "`%s/%s' -> `%s/%s/%s'", database_path, dir_name,
|
||||
database_path, dir_name, entry_name);
|
||||
}
|
||||
|
||||
close(dir_fd);
|
||||
|
||||
unlinkat(database_fd, tmp, 0);
|
||||
|
||||
if ( hash )
|
||||
printf("%s\n", digest);
|
||||
else if ( !quiet )
|
||||
printf("`%s' -> `%s/%s/%s'\n", file_path,
|
||||
database_path, dir_name, entry_name);
|
||||
|
||||
if ( do_link || do_symlink )
|
||||
{
|
||||
if ( unlink(file_path) < 0 )
|
||||
error(1, errno, "cannot unlink: `%s'", file_path);
|
||||
|
||||
size_t link_dest_length = strlen(database_path) + 1 + strlen(dir_name) + strlen(entry_name);
|
||||
char* link_dest = (char*) malloc(sizeof(char) * (link_dest_length + 1));
|
||||
|
||||
stpcpy(stpcpy(stpcpy(stpcpy(stpcpy(link_dest, database_path), "/"), dir_name), "/"), entry_name);
|
||||
|
||||
if ( do_symlink )
|
||||
{
|
||||
if ( symlink(link_dest, file_path) < 0 )
|
||||
error(1, errno, "cannot symlink `%s' to `%s'", file_path,
|
||||
link_dest);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( link(link_dest, file_path) < 0 )
|
||||
error(1, errno, "cannot link `%s' to `%s'", file_path,
|
||||
link_dest);
|
||||
}
|
||||
|
||||
free(link_dest);
|
||||
}
|
||||
}
|
||||
|
||||
close(database_fd);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,240 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2013.
|
||||
|
||||
This file is part of Tix.
|
||||
|
||||
Tix is free software: you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
Tix is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Tix. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
tix-rmpatch.cpp
|
||||
Removes files from the current source directory.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#define __STDC_CONSTANT_MACROS
|
||||
#define __STDC_LIMIT_MACROS
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <error.h>
|
||||
#include <fcntl.h>
|
||||
#include <libgen.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
int fgetc_or_die(FILE* input, const char* input_path, size_t* line,
|
||||
size_t* column)
|
||||
{
|
||||
int result = fgetc(input);
|
||||
if ( result == '\n' )
|
||||
(*line)++, *column = 0;
|
||||
else
|
||||
(*column)++;
|
||||
if ( result == EOF && ferror(input) )
|
||||
error(1, errno, "read: `%s'", input_path);
|
||||
return result;
|
||||
}
|
||||
|
||||
int fgetc_or_die_eof(FILE* input, const char* input_path, size_t* line,
|
||||
size_t* column)
|
||||
{
|
||||
int result = fgetc_or_die(input, input_path, line, column);
|
||||
if ( result == EOF )
|
||||
error(1, errno, "%s:%zu:%zu: unexpected end of file",
|
||||
input_path, *line, *column);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool check_eof(FILE* input, const char* input_path)
|
||||
{
|
||||
size_t line = 0;
|
||||
size_t column = 0;
|
||||
int c = fgetc_or_die(input, input_path, &line, &column);
|
||||
if ( c != EOF )
|
||||
{
|
||||
ungetc(c, input);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void parse_fixed(const char* text, FILE* input, const char* input_path,
|
||||
size_t* line, size_t* column)
|
||||
{
|
||||
for ( size_t i = 0; text[i]; i++ )
|
||||
{
|
||||
int ic = fgetc_or_die(input, input_path, line, column);
|
||||
if ( ic == EOF )
|
||||
error(1, errno, "%s:%zu:%zu: unexpected end of file, expected `%s'",
|
||||
input_path, *line, *column, text + i);
|
||||
if ( ic != (unsigned char) text[i] )
|
||||
error(1, errno, "%s:%zu:%zu: parse error, expected `%s'", input_path,
|
||||
*line, *column, text + i);
|
||||
}
|
||||
}
|
||||
|
||||
bool rmpatch(FILE* input, const char* input_path, bool check)
|
||||
{
|
||||
char* buffer = NULL;
|
||||
size_t buffer_used = 0;
|
||||
size_t buffer_length = 0;
|
||||
|
||||
bool result = true;
|
||||
size_t line = 1;
|
||||
while ( !check_eof(input, input_path) )
|
||||
{
|
||||
buffer_used = 0;
|
||||
size_t column = 0;
|
||||
parse_fixed("rm -rf -- '", input, input_path, &line, &column);
|
||||
while ( true )
|
||||
{
|
||||
int ic = fgetc_or_die_eof(input, input_path, &line, &column);
|
||||
if ( ic == '\'' )
|
||||
{
|
||||
ic = fgetc_or_die(input, input_path, &line, &column);
|
||||
if ( ic == EOF || ic == '\n' )
|
||||
break;
|
||||
ungetc(ic, input);
|
||||
parse_fixed("\\''", input, input_path, &line, &column);
|
||||
ic = '\'';
|
||||
}
|
||||
if ( buffer_used == buffer_length )
|
||||
{
|
||||
size_t new_length = buffer_length ? 2 * buffer_length : 16;
|
||||
buffer = (char*) realloc(buffer, sizeof(char) * (new_length + 1));
|
||||
buffer_length = new_length;
|
||||
}
|
||||
buffer[buffer_used++] = ic;
|
||||
}
|
||||
if ( !buffer_used )
|
||||
error(1, errno, "%s:%zu: unexpected empty path", input_path, line);
|
||||
assert(buffer_length);
|
||||
buffer[buffer_used] = '\0';
|
||||
if ( buffer[0] == '/' )
|
||||
error(1, errno, "%s:%zu: unexpected absolute path", input_path, line);
|
||||
if ( does_path_contain_dotdot(buffer) )
|
||||
error(1, errno, "%s:%zu: unexpected path with ..", input_path, line);
|
||||
if ( check )
|
||||
continue;
|
||||
if ( pid_t child_pid = fork_or_death() )
|
||||
{
|
||||
int status;
|
||||
waitpid(child_pid, &status, 0);
|
||||
if ( WIFSIGNALED(status) )
|
||||
error(128 + WTERMSIG(status), 0, "child with pid %ju was killed by "
|
||||
"signal %i (%s).", (uintmax_t) child_pid, WTERMSIG(status),
|
||||
strsignal(WTERMSIG(status)));
|
||||
if ( WIFEXITED(status) && WEXITSTATUS(status) != 0 )
|
||||
result = false;
|
||||
continue;
|
||||
}
|
||||
const char* cmd_argv[] =
|
||||
{
|
||||
"rm",
|
||||
"-rf",
|
||||
"--",
|
||||
buffer,
|
||||
NULL,
|
||||
};
|
||||
execvp(cmd_argv[0], (char* const*) cmd_argv);
|
||||
error(127, errno, "%s", cmd_argv[0]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void help(FILE* fp, const char* argv0)
|
||||
{
|
||||
fprintf(fp, "Usage: %s [OPTION]... [PATCH]\n", argv0);
|
||||
fprintf(fp, "Removes files from the current source directory.\n");
|
||||
}
|
||||
|
||||
void version(FILE* fp, const char* argv0)
|
||||
{
|
||||
help(fp, argv0);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
bool check = false;
|
||||
char* directory = NULL;
|
||||
|
||||
const char* argv0 = argv[0];
|
||||
for ( int i = 0; i < argc; i++ )
|
||||
{
|
||||
const char* arg = argv[i];
|
||||
if ( arg[0] != '-' )
|
||||
continue;
|
||||
argv[i] = NULL;
|
||||
if ( !strcmp(arg, "--") )
|
||||
break;
|
||||
if ( arg[1] != '-' )
|
||||
{
|
||||
while ( char c = *++arg ) switch ( c )
|
||||
{
|
||||
case 'c': check = true; break;
|
||||
default:
|
||||
fprintf(stderr, "%s: unknown option -- `%c'\n", argv0, c);
|
||||
help(stderr, argv0);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
else if ( !strcmp(arg, "--help") ) { help(stdout, argv0); exit(0); }
|
||||
else if ( !strcmp(arg, "--version") ) { version(stdout, argv0); exit(0); }
|
||||
else if ( !strcmp(arg, "--check") ) { check = true; }
|
||||
else if ( GET_OPTION_VARIABLE("--directory", &directory) ) { }
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "%s: unknown option: `%s'\n", argv0, arg);
|
||||
help(stderr, argv0);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
CompactArguments(&argc, &argv);
|
||||
|
||||
if ( 2 < argc )
|
||||
{
|
||||
fprintf(stderr, "%s: unexpected extra operand `%s'\n", argv0, argv[2]);
|
||||
help(stderr, argv0);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
const char* input_path = "<stdin>";
|
||||
FILE* input = stdin;
|
||||
|
||||
if ( argc == 2 )
|
||||
{
|
||||
input_path = argv[1];
|
||||
if ( !(input = fopen(input_path, "r")) )
|
||||
error(1, errno, "`%s'", input_path);
|
||||
}
|
||||
|
||||
if ( directory && chdir(directory) != 0 )
|
||||
error(1, errno, "chdir: `%s'", directory);
|
||||
free(directory);
|
||||
|
||||
return rmpatch(input, input_path, check) ? 0 : 1;
|
||||
}
|
|
@ -0,0 +1,285 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2013.
|
||||
|
||||
This file is part of Tix.
|
||||
|
||||
Tix is free software: you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
Tix is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
Tix. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
tix.cpp
|
||||
Front end to the Tix package management system.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#define __STDC_CONSTANT_MACROS
|
||||
#define __STDC_LIMIT_MACROS
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <error.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char* collection;
|
||||
char* tar;
|
||||
char* tixdb_path;
|
||||
char* tix_install;
|
||||
string_array_t coll_conf;
|
||||
string_array_t repo_list;
|
||||
string_array_t inst_list;
|
||||
} params_t;
|
||||
|
||||
char* FindPackageInRepository(const char* repo, const char* pkg_name)
|
||||
{
|
||||
char* repo_index_path = join_paths(repo, "repository.index");
|
||||
string_array_t repo_index = string_array_make();
|
||||
if ( !dictionary_append_file_path(&repo_index, repo_index_path) )
|
||||
error(1, errno, "bad repository: `%s'", repo_index_path);
|
||||
free(repo_index_path);
|
||||
const char* pkg_path_rel = dictionary_get(&repo_index, pkg_name);
|
||||
char* ret = pkg_path_rel ? join_paths(repo, pkg_path_rel) : NULL;
|
||||
string_array_reset(&repo_index);
|
||||
return ret;
|
||||
}
|
||||
|
||||
char* FindPackage(string_array_t* repositories, const char* pkg_name)
|
||||
{
|
||||
for ( size_t i = 0; i < repositories->length; i++ )
|
||||
{
|
||||
const char* repo = repositories->strings[i];
|
||||
char* ret = FindPackageInRepository(repo, pkg_name);
|
||||
if ( ret )
|
||||
return ret;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
string_array_t GetPackageDependencies(params_t* params, const char* pkg_name)
|
||||
{
|
||||
string_array_t ret = string_array_make();
|
||||
|
||||
char* pkg_path = FindPackage(¶ms->repo_list, pkg_name);
|
||||
if ( !pkg_path )
|
||||
error(1, errno, "unable to locate package `%s'", pkg_name);
|
||||
|
||||
const char* tixinfo_path = "tix/tixinfo";
|
||||
if ( !TarContainsFile(params->tar, pkg_path, tixinfo_path) )
|
||||
error(1, 0, "`%s' doesn't contain a `%s' file", pkg_path, tixinfo_path);
|
||||
|
||||
string_array_t tixinfo = string_array_make();
|
||||
FILE* tixinfo_fp = TarOpenFile(params->tar, pkg_path, tixinfo_path);
|
||||
dictionary_append_file(&tixinfo, tixinfo_fp);
|
||||
fclose(tixinfo_fp);
|
||||
|
||||
VerifyTixInformation(&tixinfo, pkg_path);
|
||||
|
||||
const char* deps = dictionary_get(&tixinfo, "pkg.runtime-deps", "");
|
||||
string_array_append_token_string(&ret, deps);
|
||||
|
||||
string_array_reset(&tixinfo);
|
||||
|
||||
free(pkg_path);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void GetPackageRecursiveDependencies(params_t* params, string_array_t* sofar,
|
||||
const char* pkg_name)
|
||||
{
|
||||
if ( string_array_contains(sofar, pkg_name) )
|
||||
return;
|
||||
|
||||
// Avoid endless recursion by adding our package before the recursive call,
|
||||
// in case we need to satisfy cyclic dependencies.
|
||||
string_array_append(sofar, pkg_name);
|
||||
|
||||
string_array_t pkg_deps = GetPackageDependencies(params, pkg_name);
|
||||
for ( size_t i = 0; i < pkg_deps.length; i++ )
|
||||
if ( !string_array_contains(sofar, pkg_deps.strings[i]) )
|
||||
GetPackageRecursiveDependencies(params, sofar, pkg_deps.strings[i]);
|
||||
string_array_reset(&pkg_deps);
|
||||
}
|
||||
|
||||
void InstallPackageOfName(params_t* params, const char* pkg_name)
|
||||
{
|
||||
char* pkg_path = FindPackage(¶ms->repo_list, pkg_name);
|
||||
if ( !pkg_path )
|
||||
error(1, errno, "unable to locate package `%s'", pkg_name);
|
||||
|
||||
if ( fork_and_wait_or_death() )
|
||||
{
|
||||
const char* cmd_argv[] =
|
||||
{
|
||||
params->tix_install,
|
||||
"--collection", params->collection,
|
||||
"--tar", params->tar,
|
||||
"--", pkg_path,
|
||||
NULL
|
||||
};
|
||||
execvp(cmd_argv[0], (char* const*) cmd_argv);
|
||||
error(127, errno, "`%s'", cmd_argv[0]);
|
||||
}
|
||||
|
||||
free(pkg_path);
|
||||
}
|
||||
|
||||
void Usage(FILE* fp, const char* argv0)
|
||||
{
|
||||
fprintf(fp, "Usage: %s PREFIX COMMAND [OPTION]...\n", argv0);
|
||||
fprintf(fp, "Front end to the Tix package management system.\n");
|
||||
}
|
||||
|
||||
void Help(FILE* fp, const char* argv0)
|
||||
{
|
||||
Usage(fp, argv0);
|
||||
}
|
||||
|
||||
void Version(FILE* fp, const char* argv0)
|
||||
{
|
||||
Usage(fp, argv0);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
params_t params;
|
||||
params.collection = strdup_null(getenv_def("TIX_COLLECTION", NULL));
|
||||
params.tar = strdup(getenv_def("TAR", "tar"));
|
||||
params.tix_install = strdup("tix-install");
|
||||
|
||||
const char* argv0 = argv[0];
|
||||
for ( int i = 0; i < argc; i++ )
|
||||
{
|
||||
const char* arg = argv[i];
|
||||
if ( arg[0] != '-' )
|
||||
continue;
|
||||
argv[i] = NULL;
|
||||
if ( !strcmp(arg, "--") )
|
||||
break;
|
||||
if ( arg[1] != '-' )
|
||||
{
|
||||
while ( char c = *++arg ) switch ( c )
|
||||
{
|
||||
default:
|
||||
fprintf(stderr, "%s: unknown option -- `%c'\n", argv0, c);
|
||||
Usage(stderr, argv0);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
else if ( !strcmp(arg, "--help") ) { Help(stdout, argv0); exit(0); }
|
||||
else if ( !strcmp(arg, "--usage") ) { Usage(stdout, argv0); exit(0); }
|
||||
else if ( !strcmp(arg, "--version") ) { Version(stdout, argv0); exit(0); }
|
||||
else if ( GET_OPTION_VARIABLE("--collection", ¶ms.collection) ) { }
|
||||
else if ( GET_OPTION_VARIABLE("--tar", ¶ms.tar) ) { }
|
||||
else if ( GET_OPTION_VARIABLE("--tix-install", ¶ms.tix_install) ) { }
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "%s: unknown option: `%s'\n", argv0, arg);
|
||||
Usage(stderr, argv0);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if ( argc == 1 )
|
||||
{
|
||||
Usage(stdout, argv0);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
CompactArguments(&argc, &argv);
|
||||
|
||||
ParseOptionalCommandLineCollectionPrefix(¶ms.collection, &argc, &argv);
|
||||
VerifyCommandLineCollection(¶ms.collection);
|
||||
|
||||
params.tixdb_path = join_paths(params.collection, "tix");
|
||||
|
||||
char* coll_conf_path = join_paths(params.tixdb_path, "collection.conf");
|
||||
params.coll_conf = string_array_make();
|
||||
if ( !dictionary_append_file_path(¶ms.coll_conf, coll_conf_path) )
|
||||
error(1, errno, "`%s'", coll_conf_path);
|
||||
VerifyTixCollectionConfiguration(¶ms.coll_conf, coll_conf_path);
|
||||
free(coll_conf_path);
|
||||
|
||||
char* repo_list_path = join_paths(params.tixdb_path, "repository.list");
|
||||
params.repo_list = string_array_make();
|
||||
if ( !string_array_append_file_path(¶ms.repo_list, repo_list_path) )
|
||||
error(1, errno, "`%s'", repo_list_path);
|
||||
free(repo_list_path);
|
||||
|
||||
char* inst_list_path = join_paths(params.tixdb_path, "installed.list");
|
||||
params.inst_list = string_array_make();
|
||||
if ( !string_array_append_file_path(¶ms.inst_list, inst_list_path) )
|
||||
error(1, errno, "`%s'", inst_list_path);
|
||||
free(inst_list_path);
|
||||
|
||||
if ( argc == 1 )
|
||||
{
|
||||
error(0, 0, "error: no command specified.");
|
||||
Usage(stderr, argv0);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
const char* cmd = argv[1];
|
||||
if ( !strcmp(cmd, "install") )
|
||||
{
|
||||
if ( argc == 2 )
|
||||
{
|
||||
error(0, 0, "expected list of packages to install after `install'");
|
||||
Usage(stderr, argv0);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
string_array_t work = string_array_make();
|
||||
|
||||
for ( int i = 2; i < argc; i++ )
|
||||
{
|
||||
const char* pkg_name = argv[i];
|
||||
if ( string_array_contains(¶ms.inst_list, pkg_name) )
|
||||
{
|
||||
printf("Package `%s' is already installed.\n", pkg_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
GetPackageRecursiveDependencies(¶ms, &work, pkg_name);
|
||||
}
|
||||
|
||||
for ( size_t i = 0; i < work.length; i++ )
|
||||
InstallPackageOfName(¶ms, work.strings[i]);
|
||||
|
||||
string_array_reset(&work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "%s: unknown command: `%s'\n", argv0, cmd);
|
||||
Usage(stderr, argv0);
|
||||
exit(1);
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue