diff --git a/src/xcselect/CMakeLists.txt b/src/xcselect/CMakeLists.txt index fa2d66046..2763d864c 100644 --- a/src/xcselect/CMakeLists.txt +++ b/src/xcselect/CMakeLists.txt @@ -1,17 +1,20 @@ project(xcselect) -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -nostdinc") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -nostdinc -fblocks") set(DYLIB_INSTALL_NAME "/usr/lib/libxcselect.dylib") set(DYLIB_COMPAT_VERSION "1.0.0") set(DYLIB_CURRENT_VERSION "1.0.0") -add_darling_library(xcselect libxcselect.c) +add_darling_library(xcselect xcselect.c) target_link_libraries(xcselect system) add_darling_executable(xcrun xcrun.c) target_link_libraries(xcrun system xcselect) +add_darling_executable(xcode-select xcode-select.c) +target_link_libraries(xcode-select system xcselect) + function(add_shim name) add_darling_executable("${name}_shim" xcrun-shim.c) @@ -27,5 +30,5 @@ endfunction(add_shim) add_shim(nm) install(TARGETS xcselect DESTINATION libexec/darling/usr/lib) -install(TARGETS xcrun DESTINATION libexec/darling/usr/bin) +install(TARGETS xcrun xcode-select DESTINATION libexec/darling/usr/bin) diff --git a/src/xcselect/xcode-select.c b/src/xcselect/xcode-select.c new file mode 100644 index 000000000..26a299e5b --- /dev/null +++ b/src/xcselect/xcode-select.c @@ -0,0 +1,189 @@ +/* +This file is part of Darling. + +Copyright (C) 2017 Lubos Dolezel + +Darling 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. + +Darling 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 Darling. If not, see . +*/ + +#include +#include +#include +#include +#include +#include +#include "xcselect.h" + +void printUsage(void); +void doReset(void); +void doSwitch(const char* path); +void doPrintManPaths(void); + +int main(int argc, const char** argv) +{ + if (argc == 1) + { + fprintf(stderr, "xcode-select: error: no command option given\n"); + printUsage(); + } + else if (argc == 2 || argc == 3) + { + if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0) + { + printUsage(); + } + else if (strcmp(argv[1], "-v") == 0 || strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-version") == 0) + { + printf("xcode-select for Darling, version 1.0\n"); + } + else if (strcmp(argv[1], "-p") == 0 || strcmp(argv[1], "--print-path") == 0 || strcmp(argv[1], "-print-path") == 0) + { + char path[1024]; + bool is_cmd_line; + + if (xcselect_get_developer_dir_path(path, sizeof(path), &is_cmd_line)) + { + printf("%s\n", path); + } + else + { + fprintf(stderr, "xcode-select: error: unable to get active developer directory\n"); + return 1; + } + } + else if (strcmp(argv[1], "--show-manpaths") == 0) + { + doPrintManPaths(); + } + else if (strcmp(argv[1], "--install") == 0) + { + if (access("/Library/Developer/CommandLineTools/usr/lib/libxcrun.dylib", F_OK) == 0) + { + fprintf(stderr, "xcode-select: error: command line tools are already installed\n"); + return 1; + } + else + { + printf("Please visit https://developer.apple.com/download/more/ and download\n" + "a package named \"Command Line Tools\".\n\n" + "After that, install them by running `installer -pkg file.pkg -target /`.\n"); + } + } + else if (strcmp(argv[1], "-r") == 0 || strcmp(argv[1], "--reset") == 0) + { + doReset(); + } + else if (strcmp(argv[1], "-s") == 0 || strcmp(argv[1], "--switch") == 0 || strcmp(argv[1], "-switch") == 0) + { + if (argc != 3) + { + fprintf(stderr, "xcode-select: error: missing argument to '%s'\n", argv[1]); + return 1; + } + + doSwitch(argv[2]); + } + else + { + fprintf(stderr, "xcode-select: error: unknown option: %s\n", argv[1]); + printUsage(); + return 1; + } + + return 0; + } + else + { + fprintf(stderr, "xcode-select: error: bad argument count\n"); + printUsage(); + } + return 1; +} + +void printUsage(void) +{ + fprintf(stderr, "Usage: xcode-select [options]\n\n"); + fprintf(stderr, "xcode-select is used to set up path to the active developer directory.\n" + "This affects both toolchain commands (such as clang or make) and Xcode-specific\n" + "tools (such as xcodebuild).\n\n"); + fprintf(stderr, "Options:\n" + " -h, --help print this help message\n" + " -p, --print-path print the path of the active developer directory\n" + " -s , --switch change the path of the active developer directory\n" + " --install trigger the installation of command line developer tools\n" + " -v, --version print the version of this tool\n" + " -r, --reset reset to the default developer directory\n"); +} + +static void killLink(const char* path) +{ + if (unlink(path) != 0) + { + if (errno != ENOENT) + { + fprintf(stderr, "xcode-select: error: cannot remove existing link at '%s': %s\n", + path, strerror(errno)); + exit(1); + } + } +} + +void doReset(void) +{ + killLink("/var/db/xcode_select_link"); + killLink("/usr/share/xcode-select/xcode_dir_link"); + killLink("/usr/share/xcode-select/xcode_dir_path"); +} + +void doSwitch(const char* path) +{ + char buffer[1024]; + bool unused; + + if (!xcselect_find_developer_contents_from_path(path, buffer, unused, sizeof(buffer))) + { + fprintf(stderr, "xcode-select: error: invalid developer directory '%s'\n", path); + exit(1); + } + + doReset(); + + umask(022); + if (symlink(buffer, "/var/db/xcode_select_link") != 0) + { + fprintf(stderr, "xcode-select: error: unable to create symlink: %s\n", + strerror(errno)); + exit(1); + } +} + +void doPrintManPaths(void) +{ + xcselect_manpaths *xcp; + const char *path; + unsigned i, count; + + xcp = xcselect_get_manpaths(NULL); + if (xcp != NULL) { + count = xcselect_manpaths_get_num_paths(xcp); + for (i = 0; i < count; i++) { + path = xcselect_manpaths_get_path(xcp, i); + if (path != NULL) { + puts(path); + } + } + xcselect_manpaths_free(xcp); + } +} + diff --git a/src/xcselect/xcrun-shim.c b/src/xcselect/xcrun-shim.c index 857b2309f..b904b5d27 100644 --- a/src/xcselect/xcrun-shim.c +++ b/src/xcselect/xcrun-shim.c @@ -16,7 +16,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Darling. If not, see . */ -#include "libxcselect.h" +#include "xcselect.h" #include #include diff --git a/src/xcselect/xcrun.c b/src/xcselect/xcrun.c index 07191a343..9a7469c4a 100644 --- a/src/xcselect/xcrun.c +++ b/src/xcselect/xcrun.c @@ -16,7 +16,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Darling. If not, see . */ -#include "libxcselect.h" +#include "xcselect.h" #include #include diff --git a/src/xcselect/libxcselect.c b/src/xcselect/xcselect.c similarity index 60% rename from src/xcselect/libxcselect.c rename to src/xcselect/xcselect.c index a81e32116..f548bddff 100644 --- a/src/xcselect/libxcselect.c +++ b/src/xcselect/xcselect.c @@ -26,7 +26,7 @@ along with Darling. If not, see . #include #include #include -#include "libxcselect.h" +#include "xcselect.h" static char path_buffer[1024]; @@ -92,50 +92,50 @@ static bool valid_dev_path(const char* path) return false; } -const char* xcselect_find_developer_contents_from_path(const char* p, bool* is_cmd_line) +bool xcselect_find_developer_contents_from_path(const char* p, char* dst, bool* is_cmd_line, size_t dst_size) { size_t length; if (*p != '/') { - getcwd(path_buffer, sizeof(path_buffer)); - strcat(path_buffer, "/"); - strcat(path_buffer, p); + getcwd(dst, dst_size); + strcat(dst, "/"); + strcat(dst, p); } else - strcpy(path_buffer, p); + strlcpy(dst, p, dst_size); - length = strlen(path_buffer); - if (valid_dev_path(path_buffer)) - return path_buffer; + length = strlen(dst); + if (valid_dev_path(dst)) + return true; - path_buffer[length++] = '/'; - path_buffer[length] = 0; + dst[length++] = '/'; + dst[length] = 0; - strcat(path_buffer, "Library/Developer/CommandLineTools"); - if (valid_dev_path(path_buffer)) + strcat(dst, "Library/Developer/CommandLineTools"); + if (valid_dev_path(dst)) { *is_cmd_line = true; - return path_buffer; + return true; } - path_buffer[length] = 0; - strcat(path_buffer, "CommandLineTools"); - if (valid_dev_path(path_buffer)) + dst[length] = 0; + strcat(dst, "CommandLineTools"); + if (valid_dev_path(dst)) { *is_cmd_line = true; - return path_buffer; + return true; } - path_buffer[length] = 0; - strcat(path_buffer, "Contents/Developer"); - if (valid_dev_path(path_buffer)) - return path_buffer; + dst[length] = 0; + strcat(dst, "Contents/Developer"); + if (valid_dev_path(dst)) + return true; - return NULL; + return false; } -const char* xcselect_get_developer_dir_path(bool* is_cmd_line) +bool xcselect_get_developer_dir_path(char* path, size_t path_len, bool* is_cmd_line) { const char* p; char* slash; @@ -145,9 +145,11 @@ const char* xcselect_get_developer_dir_path(bool* is_cmd_line) p = getenv("DEVELOPER_DIR"); if (p) { - p = xcselect_find_developer_contents_from_path(p, is_cmd_line); - if (p) + if (xcselect_find_developer_contents_from_path(p, path_buffer, is_cmd_line, sizeof(path_buffer))) + { + p = path_buffer; goto have_path; + } } p = get_developer_dir_from_symlink("/var/db/xcode_select_link"); @@ -173,7 +175,7 @@ const char* xcselect_get_developer_dir_path(bool* is_cmd_line) goto have_path; } - return NULL; + return false; have_path: slash = strrchr(p, '/'); @@ -183,18 +185,18 @@ have_path: *is_cmd_line = true; } - return p; + strlcpy(path, p, path_len); + strlcat(path, "/", path_len); + return true; } int xcselect_invoke_xcrun(const char* tool, int argc, char* argv[], int flags) { - const char* dev_dir; + char dev_dir[1024]; bool is_cmdline; - dev_dir = xcselect_get_developer_dir_path(&is_cmdline); - - if (dev_dir != NULL) + if (xcselect_get_developer_dir_path(dev_dir, sizeof(dev_dir), &is_cmdline)) { char* buf = (char*) malloc(2048); size_t length; @@ -266,3 +268,94 @@ int xcselect_invoke_xcrun(const char* tool, int argc, char* argv[], int flags) exit(1); } +struct __xcselect_manpaths +{ + unsigned int count; + char** paths; +}; + +void xcselect_manpaths_append(xcselect_manpaths* paths, const char* path) +{ + paths->count++; + paths->paths = realloc(paths->paths, sizeof(char*) * paths->count); + paths->paths[paths->count - 1] = strdup(path); +} + +xcselect_manpaths* xcselect_get_manpaths(const char* sdkname) +{ + char path[1024]; + bool unused; + size_t len; + xcselect_manpaths* rv; + + if (!xcselect_get_developer_dir_path(path, sizeof(path), &unused)) + return NULL; + + len = strlen(path); + strcat(path, "usr/lib/libxcrun.dylib"); + + rv = (xcselect_manpaths*) malloc(sizeof(*rv)); + memset(rv, 0, sizeof(*rv)); + + if (access(path, F_OK) == 0) + { + void* module = dlopen(path, RTLD_LAZY); + void (*fn)(const char* devpath, const char* sdkname, void (^)(const char*)); + + if (!module) + { + free(rv); + fprintf(stderr, "%s: error: cannot load libxcrun (%s)\n", getprogname(), dlerror()); + return NULL; + } + + *((void**)&fn) = dlsym(module, "xcrun_iter_manpaths"); + + if (fn != NULL) + { + path[len] = 0; + fn(path, sdkname, ^(const char* path) { + xcselect_manpaths_append(rv, path); + }); + } + + dlclose(module); + } + + // Add standard paths + path[len] = 0; + strcat(path, "usr/share/man"); + xcselect_manpaths_append(rv, path); + + path[len] = 0; + strcat(path, "usr/llvm-gcc-4,2/share/man"); + xcselect_manpaths_append(rv, path); + + path[len] = 0; + strcat(path, "Toolchains/XcodeDefault.xctoolchain/usr/share/man"); + xcselect_manpaths_append(rv, path); + + return rv; +} + +unsigned int xcselect_manpaths_get_num_paths(xcselect_manpaths* p) +{ + return p->count; +} + +const char* xcselect_manpaths_get_path(xcselect_manpaths* p, unsigned int idx) +{ + if (idx < xcselect_manpaths_get_num_paths(p)) + return p->paths[idx]; + else + return NULL; +} + +void xcselect_manpaths_free(xcselect_manpaths* p) +{ + for (unsigned int i = 0; i < p->count; i++) + free(p->paths[i]); + free(p->paths); + free(p); +} + diff --git a/src/xcselect/libxcselect.h b/src/xcselect/xcselect.h similarity index 60% rename from src/xcselect/libxcselect.h rename to src/xcselect/xcselect.h index c379558bd..f14fbd651 100644 --- a/src/xcselect/libxcselect.h +++ b/src/xcselect/xcselect.h @@ -18,10 +18,21 @@ along with Darling. If not, see . */ #ifndef _LIBXCSELECT_H_ #define _LIBXCSELECT_H_ +#include +#include #define XCSELECT_FLAG_REQUIRE_XCODE 1 int xcselect_invoke_xcrun(const char* tool, int argc, char* argv[], int flags); +bool xcselect_get_developer_dir_path(char* path, size_t len, bool* is_cmd_line); +bool xcselect_find_developer_contents_from_path(const char* p, char* dst, bool* is_cmd_line, size_t dst_size); + +typedef struct __xcselect_manpaths xcselect_manpaths; +xcselect_manpaths* xcselect_get_manpaths(const char* sdkname); +unsigned int xcselect_manpaths_get_num_paths(xcselect_manpaths* p); +const char* xcselect_manpaths_get_path(xcselect_manpaths* p, unsigned int idx); +void xcselect_manpaths_free(xcselect_manpaths* p); + #endif