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