From 257cb1c8e8db768f1a32245d388c46d7c6e60c6a Mon Sep 17 00:00:00 2001 From: Andrew Childs Date: Thu, 19 Nov 2020 16:48:10 +0900 Subject: print-reexports: rehome, tbd4 support, cross compilation --- .../os-specific/darwin/print-reexports/default.nix | 17 ++ pkgs/os-specific/darwin/print-reexports/main.c | 213 +++++++++++++++++++++ .../darwin/print-reexports/setup-hook.sh | 19 ++ 3 files changed, 249 insertions(+) create mode 100644 pkgs/os-specific/darwin/print-reexports/default.nix create mode 100644 pkgs/os-specific/darwin/print-reexports/main.c create mode 100644 pkgs/os-specific/darwin/print-reexports/setup-hook.sh (limited to 'pkgs/os-specific/darwin/print-reexports') diff --git a/pkgs/os-specific/darwin/print-reexports/default.nix b/pkgs/os-specific/darwin/print-reexports/default.nix new file mode 100644 index 00000000000..740bcb48ef5 --- /dev/null +++ b/pkgs/os-specific/darwin/print-reexports/default.nix @@ -0,0 +1,17 @@ +{ lib, stdenv, libyaml }: + +stdenv.mkDerivation { + name = "print-reexports"; + src = lib.sourceFilesBySuffices ./. [".c"]; + + buildInputs = [ libyaml ]; + + buildPhase = '' + $CC -lyaml -o print-reexports main.c + ''; + + installPhase = '' + mkdir -p $out/bin + mv print-reexports $out/bin + ''; +} diff --git a/pkgs/os-specific/darwin/print-reexports/main.c b/pkgs/os-specific/darwin/print-reexports/main.c new file mode 100644 index 00000000000..e6ff527da96 --- /dev/null +++ b/pkgs/os-specific/darwin/print-reexports/main.c @@ -0,0 +1,213 @@ +/** + * Display the list of re-exported libraries from a TAPI v2 .tbd file, one per + * line on stdout. + * + * TAPI files are the equivalent of library files for the purposes of linking. + * Like dylib files, they may re-export other libraries. In upstream usage + * these refer to the absolute paths of dylibs, and are resolved to .tbd files + * in combination with the syslibroot option. In nixpkgs, the .tbd files refer + * directly to other .tbd files without a syslibroot. Note that each .tbd file + * contains an install name, so the re-exported path does not affect the final + * result. + * + * In nixpkgs each framework is a distinct store path and some frameworks + * re-export other frameworks. The re-exported names are rewritten to refer to + * the store paths of dependencies via textual substitution. This utility is + * used to emit every file that is listed as a re-exported library, which + * allows the framework builder to verify their existence. + */ + +#include +#include +#include + +#define LOG(str, ...) fprintf(stderr, "%s", str) + +#define LOGF(...) fprintf(stderr, __VA_ARGS__) + +static yaml_node_t *get_mapping_entry(yaml_document_t *document, yaml_node_t *mapping, const char *name) { + if (!mapping) { + fprintf(stderr, "get_mapping_entry: mapping is null\n"); + return NULL; + } + + for ( + yaml_node_pair_t *pair = mapping->data.mapping.pairs.start; + pair < mapping->data.mapping.pairs.top; + ++pair + ) { + yaml_node_t *key = yaml_document_get_node(document, pair->key); + + if (!key) { + LOGF("key (%d) is null\n", pair->key); + return NULL; + } + + if (key->type != YAML_SCALAR_NODE) { + LOG("get_mapping_entry: key is not a scalar\n"); + return NULL; + } + + if (strncmp((const char *)key->data.scalar.value, name, key->data.scalar.length) != 0) { + continue; + } + + return yaml_document_get_node(document, pair->value); + } + + return NULL; +} + +static int emit_reexports_v2(yaml_document_t *document) { + yaml_node_t *root = yaml_document_get_root_node(document); + + yaml_node_t *exports = get_mapping_entry(document, root, "exports"); + + if (!exports) { + return 1; + } + + if (exports->type != YAML_SEQUENCE_NODE) { + LOG("value is not a sequence\n"); + return 0; + } + + for ( + yaml_node_item_t *export = exports->data.sequence.items.start; + export < exports->data.sequence.items.top; + ++export + ) { + yaml_node_t *export_node = yaml_document_get_node(document, *export); + + yaml_node_t *reexports = get_mapping_entry(document, export_node, "re-exports"); + + if (!reexports) { + continue; + } + + if (reexports->type != YAML_SEQUENCE_NODE) { + LOG("re-exports is not a sequence\n"); + return 0; + } + + for ( + yaml_node_item_t *reexport = reexports->data.sequence.items.start; + reexport < reexports->data.sequence.items.top; + ++reexport + ) { + yaml_node_t *val = yaml_document_get_node(document, *reexport); + + if (val->type != YAML_SCALAR_NODE) { + LOG("item is not a scalar\n"); + return 0; + } + + fwrite(val->data.scalar.value, val->data.scalar.length, 1, stdout); + putchar('\n'); + } + } + + return 1; +} + +static int emit_reexports_v4(yaml_document_t *document) { + yaml_node_t *root = yaml_document_get_root_node(document); + yaml_node_t *reexports = get_mapping_entry(document, root, "reexported-libraries"); + + if (!reexports) { + return 1; + } + + if (reexports->type != YAML_SEQUENCE_NODE) { + LOG("value is not a sequence\n"); + return 0; + } + + for ( + yaml_node_item_t *entry = reexports->data.sequence.items.start; + entry < reexports->data.sequence.items.top; + ++entry + ) { + yaml_node_t *entry_node = yaml_document_get_node(document, *entry); + + yaml_node_t *libs = get_mapping_entry(document, entry_node, "libraries"); + + if (!libs) { + continue; + } + + if (libs->type != YAML_SEQUENCE_NODE) { + LOG("libraries is not a sequence\n"); + return 0; + } + + for ( + yaml_node_item_t *lib = libs->data.sequence.items.start; + lib < libs->data.sequence.items.top; + ++lib + ) { + yaml_node_t *val = yaml_document_get_node(document, *lib); + + if (val->type != YAML_SCALAR_NODE) { + LOG("item is not a scalar\n"); + return 0; + } + + fwrite(val->data.scalar.value, val->data.scalar.length, 1, stdout); + putchar('\n'); + } + } + + return 1; +} + +int main(int argc, char **argv) { + int result = 0; + + if (argc != 2) { + fprintf(stderr, "Invalid usage\n"); + result = 2; + goto done; + } + + FILE *f = fopen(argv[1], "r"); + if (!f) { + perror("opening input file"); + result = errno; + goto done; + } + + yaml_parser_t yaml_parser; + if (!yaml_parser_initialize(&yaml_parser)) { + fprintf(stderr, "Failed to initialize yaml parser\n"); + result = 1; + goto err_file; + } + + yaml_parser_set_input_file(&yaml_parser, f); + + yaml_document_t yaml_document; + + if(!yaml_parser_load(&yaml_parser, &yaml_document)) { + fprintf(stderr, "Failed to load yaml file\n"); + result = 1; + goto err_yaml; + } + + // Try both, only fail if one reports an error. A lack of re-exports is not + // considered an error. + int ok = 1; + ok = ok && emit_reexports_v2(&yaml_document); + ok = ok && emit_reexports_v4(&yaml_document); + + result = !ok; + +err_yaml: + yaml_parser_delete(&yaml_parser); + +err_file: + fclose(f); + +done: + return result; +} diff --git a/pkgs/os-specific/darwin/print-reexports/setup-hook.sh b/pkgs/os-specific/darwin/print-reexports/setup-hook.sh new file mode 100644 index 00000000000..9efb00aeb4d --- /dev/null +++ b/pkgs/os-specific/darwin/print-reexports/setup-hook.sh @@ -0,0 +1,19 @@ +fixupOutputHooks+=('checkTbdReexports') + +checkTbdReexports() { + local dir="$1" + + while IFS= read -r -d $'\0' tbd; do + echo "checkTbdRexports: checking re-exports in $tbd" + while read -r target; do + local expected="${target%.dylib}.tbd" + if ! [ -e "$expected" ]; then + echo -e "Re-export missing:\n\t'$target'\n\t(expected '$expected')" + echo -e "While processing\n\t'$tbd'" + exit 1 + else + echo "Re-exported target '$target' ok" + fi + done < <(print-reexports "$tbd") + done < <(find $prefix -type f -name '*.tbd' -print0) +} -- cgit 1.4.1