diff --git a/Makefile b/Makefile index f952c283..68b1d8f6 100644 --- a/Makefile +++ b/Makefile @@ -49,23 +49,23 @@ all: debug release ci: ci_32 ci_64 ci_32: - $(V) $(MAKE) -f util/wren.mk MODE=debug LANG=c ARCH=32 vm cli test + $(V) $(MAKE) -f util/wren.mk MODE=debug LANG=c ARCH=32 vm cli api_test $(V) ./util/test.py --suffix=d-32 $(suite) - $(V) $(MAKE) -f util/wren.mk MODE=debug LANG=cpp ARCH=32 vm cli test + $(V) $(MAKE) -f util/wren.mk MODE=debug LANG=cpp ARCH=32 vm cli api_test $(V) ./util/test.py --suffix=d-cpp-32 $(suite) - $(V) $(MAKE) -f util/wren.mk MODE=release LANG=c ARCH=32 vm cli test + $(V) $(MAKE) -f util/wren.mk MODE=release LANG=c ARCH=32 vm cli api_test $(V) ./util/test.py --suffix=-32 $(suite) - $(V) $(MAKE) -f util/wren.mk MODE=release LANG=cpp ARCH=32 vm cli test + $(V) $(MAKE) -f util/wren.mk MODE=release LANG=cpp ARCH=32 vm cli api_test $(V) ./util/test.py --suffix=-cpp-32 $(suite) ci_64: - $(V) $(MAKE) -f util/wren.mk MODE=debug LANG=c ARCH=64 vm cli test + $(V) $(MAKE) -f util/wren.mk MODE=debug LANG=c ARCH=64 vm cli api_test $(V) ./util/test.py --suffix=d-64 $(suite) - $(V) $(MAKE) -f util/wren.mk MODE=debug LANG=cpp ARCH=64 vm cli test + $(V) $(MAKE) -f util/wren.mk MODE=debug LANG=cpp ARCH=64 vm cli api_test $(V) ./util/test.py --suffix=d-cpp-64 $(suite) - $(V) $(MAKE) -f util/wren.mk MODE=release LANG=c ARCH=64 vm cli test + $(V) $(MAKE) -f util/wren.mk MODE=release LANG=c ARCH=64 vm cli api_test $(V) ./util/test.py --suffix=-64 $(suite) - $(V) $(MAKE) -f util/wren.mk MODE=release LANG=cpp ARCH=64 vm cli test + $(V) $(MAKE) -f util/wren.mk MODE=release LANG=cpp ARCH=64 vm cli api_test $(V) ./util/test.py --suffix=-cpp-64 $(suite) # Remove all build outputs and intermediate files. Does not remove downloaded @@ -77,13 +77,17 @@ clean: # Run the tests against the debug build of Wren. test: debug - $(V) $(MAKE) -f util/wren.mk MODE=debug test + $(V) $(MAKE) -f util/wren.mk MODE=debug api_test $(V) ./util/test.py $(suite) benchmark: release - $(V) $(MAKE) -f util/wren.mk test + $(V) $(MAKE) -f util/wren.mk api_test $(V) ./util/benchmark.py -l wren $(suite) +unit_test: + $(V) $(MAKE) -f util/wren.mk MODE=debug unit_test + $(V) ./build/debug/test/unit_wrend + # Generate the Wren site. docs: $(V) ./util/generate_docs.py @@ -104,4 +108,4 @@ gh-pages: docs amalgamation: src/include/wren.h src/vm/*.h src/vm/*.c ./util/generate_amalgamation.py > build/wren.c -.PHONY: all amalgamation builtin clean debug docs gh-pages release test vm watchdocs ci ci_32 ci_64 +.PHONY: all amalgamation benchmark builtin clean debug docs gh-pages release test vm watchdocs ci ci_32 ci_64 diff --git a/src/cli/path.c b/src/cli/path.c new file mode 100644 index 00000000..a3362a3a --- /dev/null +++ b/src/cli/path.c @@ -0,0 +1,254 @@ +#include +#include +#include +#include + +#include "path.h" + +// The maximum number of components in a path. We can't normalize a path that +// contains more than this number of parts. The number here assumes a max path +// length of 4096, which is common on Linux, and then assumes each component is +// at least two characters, "/", and a single-letter directory name. +#define MAX_COMPONENTS 2048 + +typedef struct { + const char* start; + const char* end; +} Slice; + +static void ensureCapacity(Path* path, size_t capacity) +{ + // Capacity always needs to be one greater than the actual length to have + // room for the null byte, which is stored in the buffer, but not counted in + // the length. A zero-character path still needs a one-character array to + // store the '\0'. + capacity++; + + if (path->capacity >= capacity) return; + + // Grow by doubling in size. + size_t newCapacity = 16; + while (newCapacity < capacity) newCapacity *= 2; + + path->chars = (char*)realloc(path->chars, newCapacity); + path->capacity = newCapacity; +} + +static void appendSlice(Path* path, Slice slice) +{ + size_t length = slice.end - slice.start; + ensureCapacity(path, path->length + length); + memcpy(path->chars + path->length, slice.start, length); + path->length += length; + path->chars[path->length] = '\0'; +} + +Path* pathNew(const char* string) +{ + Path* path = (Path*)malloc(sizeof(Path)); + path->chars = (char*)malloc(1); + path->chars[0] = '\0'; + path->length = 0; + path->capacity = 0; + + pathAppendString(path, string); + + return path; +} + +void pathFree(Path* path) +{ + if (path->chars) free(path->chars); + free(path); +} + +void pathDirName(Path* path) +{ + // Find the last path separator. + for (size_t i = path->length - 1; i < path->length; i--) + { + if (path->chars[i] == '/') + { + path->length = i; + path->chars[i] = '\0'; + return; + } + } + + // If we got here, there was no separator so it must be a single component. + path->length = 0; + path->chars[0] = '\0'; +} + +void pathRemoveExtension(Path* path) +{ + for (size_t i = path->length - 1; i < path->length; i--) + { + // If we hit a path separator before finding the extension, then the last + // component doesn't have one. + if (path->chars[i] == '/') return; + + if (path->chars[i] == '.') + { + path->length = i; + path->chars[path->length] = '\0'; + } + } +} + +void pathJoin(Path* path, const char* string) +{ + if (path->length > 0 && path->chars[path->length - 1] != '/') + { + pathAppendChar(path, '/'); + } + + pathAppendString(path, string); +} + + +#ifdef _WIN32 +static bool isDriveLetter(char c) +{ + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); +} +#endif + +bool pathIsAbsolute(Path* path) +{ +#ifdef _WIN32 + // Absolute path in the current drive. + if (path->length >= 1 && path->chars[0] == '\\') return true; + + // Drive letter. + if (path->length >= 3 && + isDriveLetter(path->chars[0]) && + path->chars[1] == ':' && + path->chars[2] == '\\') + { + return true; + } + + // UNC path. + return path->length >= 2 && path->chars[0] == '\\' && path->chars[1] == '\\'; +#else + // Otherwise, assume POSIX-style paths. + return path->length >= 1 && path->chars[0] == '/'; +#endif +} + +void pathAppendChar(Path* path, char c) +{ + ensureCapacity(path, path->length + 1); + path->chars[path->length++] = c; + path->chars[path->length] = '\0'; +} + +void pathAppendString(Path* path, const char* string) +{ + Slice slice; + slice.start = string; + slice.end = string + strlen(string); + appendSlice(path, slice); +} + +Path* pathNormalize(Path* path) +{ + Path* result = pathNew(""); + + // Split the path into components. + Slice components[MAX_COMPONENTS]; + int numComponents = 0; + + char* start = path->chars; + char* end = path->chars; + + // Split into parts and handle "." and "..". + int leadingDoubles = 0; + for (;;) + { + if (*end == '\0' || *end == '/') + { + // Add the current component. + if (start != end) + { + size_t length = end - start; + if (length == 1 && start[0] == '.') + { + // Skip "." components. + } + else if (length == 2 && start[0] == '.' && start[1] == '.') + { + // Walk out of directories on "..". + if (numComponents > 0) + { + // Discard the previous component. + numComponents--; + } + else + { + // Can't back out any further, so preserve the "..". + leadingDoubles++; + } + } + else + { + if (numComponents >= MAX_COMPONENTS) + { + fprintf(stderr, "Path cannot have more than %d path components.\n", + MAX_COMPONENTS); + exit(1); + } + + components[numComponents].start = start; + components[numComponents].end = end; + numComponents++; + } + } + + // Skip over separators. + while (*end != '\0' && *end == '/') end++; + + start = end; + if (*end == '\0') break; + } + + end++; + } + + // Preserve the absolute prefix, if any. + bool needsSeparator = false; + if (path->length > 0 && path->chars[0] == '/') + { + pathAppendChar(result, '/'); + } + else + { + // Add any leading "..". + for (int i = 0; i < leadingDoubles; i++) + { + if (needsSeparator) pathAppendChar(result, '/'); + pathAppendString(result, ".."); + needsSeparator = true; + } + } + + for (int i = 0; i < numComponents; i++) + { + if (needsSeparator) pathAppendChar(result, '/'); + appendSlice(result, components[i]); + needsSeparator = true; + } + + if (result->length == 0) pathAppendChar(result, '.'); + + return result; +} + +char* pathToString(Path* path) +{ + char* string = (char*)malloc(path->length + 1); + memcpy(string, path->chars, path->length); + string[path->length] = '\0'; + return string; +} diff --git a/src/cli/path.h b/src/cli/path.h new file mode 100644 index 00000000..114346ee --- /dev/null +++ b/src/cli/path.h @@ -0,0 +1,52 @@ +#ifndef path_h +#define path_h + +// Path manipulation functions. + +typedef struct +{ + // Dynamically allocated array of characters. + char* chars; + + // The number of characters currently in use in [chars], not including the + // null terminator. + size_t length; + + // Size of the allocated [chars] buffer. + size_t capacity; +} Path; + +// Creates a new empty path. +Path* pathNew(const char* string); + +// Releases the method associated with [path]. +void pathFree(Path* path); + +// Strips off the last component of the path name. +void pathDirName(Path* path); + +// Strips off the file extension from the last component of the path. +void pathRemoveExtension(Path* path); + +// Appends [string] to [path]. +void pathJoin(Path* path, const char* string); + +// Return true if [path] is an absolute path for the host operating system. +bool pathIsAbsolute(Path* path); + +// Appends [c] to the path, growing the buffer if needed. +void pathAppendChar(Path* path, char c); + +// Appends [string] to the path, growing the buffer if needed. +void pathAppendString(Path* path, const char* string); + +// Simplifies the path string as much as possible. +// +// Applies and removes any "." or ".." components, collapses redundant "/" +// characters, etc. +Path* pathNormalize(Path* path); + +// Allocates a new string exactly the right length and copies this path to it. +char* pathToString(Path* path); + +#endif diff --git a/test/unit/main.c b/test/unit/main.c new file mode 100644 index 00000000..2da088bb --- /dev/null +++ b/test/unit/main.c @@ -0,0 +1,9 @@ +#include "path_test.h" +#include "test.h" + +int main() +{ + testPath(); + + return showTestResults(); +} diff --git a/test/unit/path_test.c b/test/unit/path_test.c new file mode 100644 index 00000000..a175dc49 --- /dev/null +++ b/test/unit/path_test.c @@ -0,0 +1,105 @@ +#include +#include +#include +#include + +#include "path.h" +#include "test.h" + +static void expectNormalize(const char* input, const char* expected) +{ + Path* path = pathNew(input); + Path* result = pathNormalize(path); + + if (strcmp(result->chars, expected) != 0) + { + printf("FAIL %-30s Want %s\n", input, expected); + printf(" Got %s\n\n", result->chars); + fail(); + } + else + { +#if SHOW_PASSES + printf("PASS %-30s -> %s\n", input, result->chars); +#endif + pass(); + } + + pathFree(path); + pathFree(result); +} + +static void testNormalize() +{ + // simple cases + expectNormalize("", "."); + expectNormalize(".", "."); + expectNormalize("..", ".."); + expectNormalize("a", "a"); + expectNormalize("/", "/"); + + // collapses redundant separators + expectNormalize("a/b/c", "a/b/c"); + expectNormalize("a//b///c////d", "a/b/c/d"); + + // eliminates "." parts + expectNormalize("./", "."); + expectNormalize("/.", "/"); + expectNormalize("/./", "/"); + expectNormalize("./.", "."); + expectNormalize("a/./b", "a/b"); + expectNormalize("a/.b/c", "a/.b/c"); + expectNormalize("a/././b/./c", "a/b/c"); + expectNormalize("././a", "a"); + expectNormalize("a/./.", "a"); + + // eliminates ".." parts + expectNormalize("..", ".."); + expectNormalize("../", ".."); + expectNormalize("../../..", "../../.."); + expectNormalize("../../../", "../../.."); + expectNormalize("/..", "/"); + expectNormalize("/../../..", "/"); + expectNormalize("/../../../a", "/a"); + expectNormalize("a/..", "."); + expectNormalize("a/b/..", "a"); + expectNormalize("a/../b", "b"); + expectNormalize("a/./../b", "b"); + expectNormalize("a/b/c/../../d/e/..", "a/d"); + expectNormalize("a/b/../../../../c", "../../c"); + + // does not walk before root on absolute paths + expectNormalize("..", ".."); + expectNormalize("../", ".."); + expectNormalize("/..", "/"); + expectNormalize("a/..", "."); + expectNormalize("../a", "../a"); + expectNormalize("/../a", "/a"); + expectNormalize("/../a", "/a"); + expectNormalize("a/b/..", "a"); + expectNormalize("../a/b/..", "../a"); + expectNormalize("a/../b", "b"); + expectNormalize("a/./../b", "b"); + expectNormalize("a/b/c/../../d/e/..", "a/d"); + expectNormalize("a/b/../../../../c", "../../c"); + expectNormalize("a/b/c/../../..d/./.e/f././", "a/..d/.e/f."); + + // removes trailing separators + expectNormalize("./", "."); + expectNormalize(".//", "."); + expectNormalize("a/", "a"); + expectNormalize("a/b/", "a/b"); + expectNormalize("a/b///", "a/b"); + + expectNormalize("foo/bar/baz", "foo/bar/baz"); + expectNormalize("foo", "foo"); + expectNormalize("foo/bar/", "foo/bar"); + expectNormalize("./foo/././bar/././", "foo/bar"); +} + +void testPath() +{ + // TODO: Test other functions. + testNormalize(); +} + diff --git a/test/unit/path_test.h b/test/unit/path_test.h new file mode 100644 index 00000000..b36436bc --- /dev/null +++ b/test/unit/path_test.h @@ -0,0 +1 @@ +void testPath(); diff --git a/test/unit/test.c b/test/unit/test.c new file mode 100644 index 00000000..b10ede95 --- /dev/null +++ b/test/unit/test.c @@ -0,0 +1,29 @@ +#include + +#include "test.h" + +int passes = 0; +int failures = 0; + +void pass() +{ + passes++; +} + +void fail() +{ + failures++; +} + +int showTestResults() +{ + if (failures > 0) + { + printf("%d out of %d tests failed. :(\n", failures, passes + failures); + return 1; + } + + printf("All %d tests passed!\n", passes + failures); + return 0; +} + diff --git a/test/unit/test.h b/test/unit/test.h new file mode 100644 index 00000000..d51319c0 --- /dev/null +++ b/test/unit/test.h @@ -0,0 +1,7 @@ +// Set this to 1 to output passing tests. +#define SHOW_PASSES 0 + +void pass(); +void fail(); + +int showTestResults(); diff --git a/util/benchmark.py b/util/benchmark.py index cb00a649..612bbea9 100755 --- a/util/benchmark.py +++ b/util/benchmark.py @@ -154,7 +154,7 @@ def run_trial(benchmark, language): # of the normal Wren build. if benchmark[0].startswith("api_"): executable_args = [ - os.path.join(WREN_DIR, "build", "release", "test", "wren") + os.path.join(WREN_DIR, "build", "release", "test", "api_wren") ] args = [] diff --git a/util/test.py b/util/test.py index b6507cd7..dac46ad5 100755 --- a/util/test.py +++ b/util/test.py @@ -25,7 +25,7 @@ config_dir = ("debug" if is_debug else "release") + config WREN_DIR = dirname(dirname(realpath(__file__))) WREN_APP = join(WREN_DIR, 'bin', 'wren' + args.suffix) -TEST_APP = join(WREN_DIR, 'build', config_dir, 'test', 'wren' + args.suffix) +TEST_APP = join(WREN_DIR, 'build', config_dir, 'test', 'api_wren' + args.suffix) EXPECT_PATTERN = re.compile(r'// expect: ?(.*)') EXPECT_ERROR_PATTERN = re.compile(r'// expect error(?! line)') diff --git a/util/wren.mk b/util/wren.mk index f37fa82b..d8273276 100644 --- a/util/wren.mk +++ b/util/wren.mk @@ -31,8 +31,11 @@ MODULE_SOURCES := $(wildcard src/module/*.c) VM_HEADERS := $(wildcard src/vm/*.h) $(wildcard src/vm/*.wren.inc) VM_SOURCES := $(wildcard src/vm/*.c) -TEST_HEADERS := $(wildcard test/api/*.h) -TEST_SOURCES := $(wildcard test/api/*.c) +API_TEST_HEADERS := $(wildcard test/api/*.h) +API_TEST_SOURCES := $(wildcard test/api/*.c) + +UNIT_TEST_HEADERS := $(wildcard test/unit/*.h) +UNIT_TEST_SOURCES := $(wildcard test/unit/*.c) BUILD_DIR := build @@ -120,11 +123,12 @@ endif CFLAGS := $(C_OPTIONS) $(C_WARNINGS) -OPT_OBJECTS := $(addprefix $(BUILD_DIR)/optional/, $(notdir $(OPT_SOURCES:.c=.o))) -CLI_OBJECTS := $(addprefix $(BUILD_DIR)/cli/, $(notdir $(CLI_SOURCES:.c=.o))) -MODULE_OBJECTS := $(addprefix $(BUILD_DIR)/module/, $(notdir $(MODULE_SOURCES:.c=.o))) -VM_OBJECTS := $(addprefix $(BUILD_DIR)/vm/, $(notdir $(VM_SOURCES:.c=.o))) -TEST_OBJECTS := $(patsubst test/api/%.c, $(BUILD_DIR)/test/%.o, $(TEST_SOURCES)) +OPT_OBJECTS := $(addprefix $(BUILD_DIR)/optional/, $(notdir $(OPT_SOURCES:.c=.o))) +CLI_OBJECTS := $(addprefix $(BUILD_DIR)/cli/, $(notdir $(CLI_SOURCES:.c=.o))) +MODULE_OBJECTS := $(addprefix $(BUILD_DIR)/module/, $(notdir $(MODULE_SOURCES:.c=.o))) +VM_OBJECTS := $(addprefix $(BUILD_DIR)/vm/, $(notdir $(VM_SOURCES:.c=.o))) +API_TEST_OBJECTS := $(patsubst test/api/%.c, $(BUILD_DIR)/test/api/%.o, $(API_TEST_SOURCES)) +UNIT_TEST_OBJECTS := $(patsubst test/unit/%.c, $(BUILD_DIR)/test/unit/%.o, $(UNIT_TEST_SOURCES)) LIBUV_DIR := deps/libuv LIBUV := build/libuv$(LIBUV_ARCH).a @@ -152,7 +156,10 @@ static: lib/lib$(WREN).a cli: bin/$(WREN) # Builds the API test executable. -test: $(BUILD_DIR)/test/$(WREN) +api_test: $(BUILD_DIR)/test/api_$(WREN) + +# Builds the unit test executable. +unit_test: $(BUILD_DIR)/test/unit_$(WREN) # Command-line interpreter. bin/$(WREN): $(OPT_OBJECTS) $(CLI_OBJECTS) $(MODULE_OBJECTS) $(VM_OBJECTS) \ @@ -173,13 +180,22 @@ lib/lib$(WREN).$(SHARED_EXT): $(OPT_OBJECTS) $(VM_OBJECTS) $(V) mkdir -p lib $(V) $(CC) $(CFLAGS) -shared $(SHARED_LIB_FLAGS) -o $@ $^ -# Test executable. -$(BUILD_DIR)/test/$(WREN): $(OPT_OBJECTS) $(MODULE_OBJECTS) $(TEST_OBJECTS) \ - $(VM_OBJECTS) $(BUILD_DIR)/cli/modules.o $(BUILD_DIR)/cli/vm.o $(LIBUV) +# API test executable. +$(BUILD_DIR)/test/api_$(WREN): $(OPT_OBJECTS) $(MODULE_OBJECTS) $(API_TEST_OBJECTS) \ + $(VM_OBJECTS) $(BUILD_DIR)/cli/modules.o $(BUILD_DIR)/cli/vm.o \ + $(BUILD_DIR)/cli/path.o $(LIBUV) @ printf "%10s %-30s %s\n" $(CC) $@ "$(C_OPTIONS)" - $(V) mkdir -p $(BUILD_DIR)/test + $(V) mkdir -p $(BUILD_DIR)/test/api $(V) $(CC) $(CFLAGS) $^ -o $@ -lm $(LIBUV_LIBS) +# Unit test executable. +$(BUILD_DIR)/test/unit_$(WREN): $(OPT_OBJECTS) $(MODULE_OBJECTS) $(UNIT_TEST_OBJECTS) \ + $(VM_OBJECTS) $(BUILD_DIR)/cli/modules.o $(BUILD_DIR)/cli/vm.o \ + $(BUILD_DIR)/cli/path.o $(LIBUV) + @ printf "%10s %-30s %s\n" $(CC) $@ "$(C_OPTIONS)" + $(V) mkdir -p $(BUILD_DIR)/test/unit + $(V) $(CC) $(CFLAGS) $^ -o $@ + # CLI object files. $(BUILD_DIR)/cli/%.o: src/cli/%.c $(CLI_HEADERS) $(MODULE_HEADERS) \ $(VM_HEADERS) $(LIBUV) @@ -194,7 +210,7 @@ $(BUILD_DIR)/module/%.o: src/module/%.c $(CLI_HEADERS) $(MODULE_HEADERS) \ $(V) mkdir -p $(BUILD_DIR)/module $(V) $(CC) -c $(CFLAGS) $(CLI_FLAGS) -o $@ $(FILE_FLAG) $< -# Aux object files. +# Optional object files. $(BUILD_DIR)/optional/%.o: src/optional/%.c $(VM_HEADERS) $(OPT_HEADERS) @ printf "%10s %-30s %s\n" $(CC) $< "$(C_OPTIONS)" $(V) mkdir -p $(BUILD_DIR)/optional @@ -206,9 +222,16 @@ $(BUILD_DIR)/vm/%.o: src/vm/%.c $(VM_HEADERS) $(V) mkdir -p $(BUILD_DIR)/vm $(V) $(CC) -c $(CFLAGS) -Isrc/include -Isrc/optional -Isrc/vm -o $@ $(FILE_FLAG) $< -# Test object files. -$(BUILD_DIR)/test/%.o: test/api/%.c $(OPT_HEADERS) $(MODULE_HEADERS) \ - $(VM_HEADERS) $(TEST_HEADERS) $(LIBUV) +# API test object files. +$(BUILD_DIR)/test/api/%.o: test/api/%.c $(OPT_HEADERS) $(MODULE_HEADERS) \ + $(VM_HEADERS) $(API_TEST_HEADERS) $(LIBUV) + @ printf "%10s %-30s %s\n" $(CC) $< "$(C_OPTIONS)" + $(V) mkdir -p $(dir $@) + $(V) $(CC) -c $(CFLAGS) $(CLI_FLAGS) -o $@ $(FILE_FLAG) $< + +# Unit test object files. +$(BUILD_DIR)/test/unit/%.o: test/unit/%.c $(OPT_HEADERS) $(MODULE_HEADERS) \ + $(VM_HEADERS) $(UNIT_TEST_HEADERS) $(LIBUV) @ printf "%10s %-30s %s\n" $(CC) $< "$(C_OPTIONS)" $(V) mkdir -p $(dir $@) $(V) $(CC) -c $(CFLAGS) $(CLI_FLAGS) -o $@ $(FILE_FLAG) $< @@ -231,4 +254,4 @@ src/module/%.wren.inc: src/module/%.wren util/wren_to_c_string.py @ printf "%10s %-30s %s\n" str $< $(V) ./util/wren_to_c_string.py $@ $< -.PHONY: all cli test vm +.PHONY: all api_test cli unit_test vm diff --git a/util/xcode/wren.xcodeproj/project.pbxproj b/util/xcode/wren.xcodeproj/project.pbxproj index 2ac08e76..3e729748 100644 --- a/util/xcode/wren.xcodeproj/project.pbxproj +++ b/util/xcode/wren.xcodeproj/project.pbxproj @@ -24,9 +24,14 @@ 293B255A1CEFD8C7005D9537 /* repl.wren.inc in Sources */ = {isa = PBXBuildFile; fileRef = 293B25561CEFD8C7005D9537 /* repl.wren.inc */; }; 293D46961BB43F9900200083 /* call.c in Sources */ = {isa = PBXBuildFile; fileRef = 293D46941BB43F9900200083 /* call.c */; }; 2940E98D2063EC030054503C /* resolution.c in Sources */ = {isa = PBXBuildFile; fileRef = 2940E98B2063EC020054503C /* resolution.c */; }; + 2940E9BB2066067D0054503C /* path_test.c in Sources */ = {isa = PBXBuildFile; fileRef = 2940E9BA2066067D0054503C /* path_test.c */; }; + 2940E9BC206607830054503C /* path.c in Sources */ = {isa = PBXBuildFile; fileRef = 2952CD1B1FA9941700985F5F /* path.c */; }; + 2940E9BE2066C3300054503C /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = 2940E9BD2066C3300054503C /* main.c */; }; + 2940E9C12066C35E0054503C /* test.c in Sources */ = {isa = PBXBuildFile; fileRef = 2940E9BF2066C35E0054503C /* test.c */; }; 2949AA8D1C2F14F000B106BA /* get_variable.c in Sources */ = {isa = PBXBuildFile; fileRef = 2949AA8B1C2F14F000B106BA /* get_variable.c */; }; 29512C811B91F8EB008C10E6 /* libuv.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 29512C801B91F8EB008C10E6 /* libuv.a */; }; 29512C821B91F901008C10E6 /* libuv.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 29512C801B91F8EB008C10E6 /* libuv.a */; }; + 2952CD1D1FA9941700985F5F /* path.c in Sources */ = {isa = PBXBuildFile; fileRef = 2952CD1B1FA9941700985F5F /* path.c */; }; 29729F311BA70A620099CA20 /* io.c in Sources */ = {isa = PBXBuildFile; fileRef = 29729F2E1BA70A620099CA20 /* io.c */; }; 29729F321BA70A620099CA20 /* io.c in Sources */ = {isa = PBXBuildFile; fileRef = 29729F2E1BA70A620099CA20 /* io.c */; }; 29729F331BA70A620099CA20 /* io.wren.inc in Sources */ = {isa = PBXBuildFile; fileRef = 29729F301BA70A620099CA20 /* io.wren.inc */; }; @@ -68,6 +73,15 @@ /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ + 2940E9B4206605DE0054503C /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; 29AB1F041816E3AD004B501E /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -118,10 +132,18 @@ 293D46951BB43F9900200083 /* call.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = call.h; path = ../../test/api/call.h; sourceTree = ""; }; 2940E98B2063EC020054503C /* resolution.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = resolution.c; path = ../../test/api/resolution.c; sourceTree = ""; }; 2940E98C2063EC020054503C /* resolution.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = resolution.h; path = ../../test/api/resolution.h; sourceTree = ""; }; + 2940E9B8206605DE0054503C /* unit_test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = unit_test; sourceTree = BUILT_PRODUCTS_DIR; }; + 2940E9B92066067D0054503C /* path_test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = path_test.h; path = ../../../test/unit/path_test.h; sourceTree = ""; }; + 2940E9BA2066067D0054503C /* path_test.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = path_test.c; path = ../../../test/unit/path_test.c; sourceTree = ""; }; + 2940E9BD2066C3300054503C /* main.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = main.c; path = ../../../test/unit/main.c; sourceTree = ""; }; + 2940E9BF2066C35E0054503C /* test.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = test.c; path = ../../../test/unit/test.c; sourceTree = ""; }; + 2940E9C02066C35E0054503C /* test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = test.h; path = ../../../test/unit/test.h; sourceTree = ""; }; 2949AA8B1C2F14F000B106BA /* get_variable.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = get_variable.c; path = ../../test/api/get_variable.c; sourceTree = ""; }; 2949AA8C1C2F14F000B106BA /* get_variable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = get_variable.h; path = ../../test/api/get_variable.h; sourceTree = ""; }; 29512C7F1B91F86E008C10E6 /* api_test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = api_test; sourceTree = BUILT_PRODUCTS_DIR; }; 29512C801B91F8EB008C10E6 /* libuv.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libuv.a; path = ../../build/libuv.a; sourceTree = ""; }; + 2952CD1B1FA9941700985F5F /* path.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = path.c; path = ../../src/cli/path.c; sourceTree = ""; }; + 2952CD1C1FA9941700985F5F /* path.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = path.h; path = ../../src/cli/path.h; sourceTree = ""; }; 296371B31AC713D000079FDA /* wren_opcodes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = wren_opcodes.h; path = ../../src/vm/wren_opcodes.h; sourceTree = ""; }; 29729F2E1BA70A620099CA20 /* io.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = io.c; path = ../../src/module/io.c; sourceTree = ""; }; 29729F301BA70A620099CA20 /* io.wren.inc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.pascal; name = io.wren.inc; path = ../../src/module/io.wren.inc; sourceTree = ""; }; @@ -164,6 +186,13 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 2940E9B2206605DE0054503C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 29AB1F031816E3AD004B501E /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -233,6 +262,8 @@ 29205C8E1AB4E5C90073018D /* main.c */, 291647C61BA5EC5E006142EE /* modules.h */, 291647C51BA5EC5E006142EE /* modules.c */, + 2952CD1C1FA9941700985F5F /* path.h */, + 2952CD1B1FA9941700985F5F /* path.c */, 29C8A9321AB71FFF00DEC81D /* vm.h */, 29C8A9311AB71FFF00DEC81D /* vm.c */, ); @@ -247,6 +278,18 @@ name = include; sourceTree = ""; }; + 2940E98E206605CB0054503C /* unit_test */ = { + isa = PBXGroup; + children = ( + 2940E9BD2066C3300054503C /* main.c */, + 2940E9BA2066067D0054503C /* path_test.c */, + 2940E9B92066067D0054503C /* path_test.h */, + 2940E9BF2066C35E0054503C /* test.c */, + 2940E9C02066C35E0054503C /* test.h */, + ); + path = unit_test; + sourceTree = ""; + }; 29AB1EFD1816E3AD004B501E = { isa = PBXGroup; children = ( @@ -256,6 +299,7 @@ 29AF31EE1BD2E37F00AAD156 /* optional */, 29205CA01AB4E6470073018D /* vm */, 29D0099A1B7E394F000CE58C /* api_test */, + 2940E98E206605CB0054503C /* unit_test */, 29512C801B91F8EB008C10E6 /* libuv.a */, 29AB1F071816E3AD004B501E /* Products */, ); @@ -266,6 +310,7 @@ children = ( 29AB1F061816E3AD004B501E /* wren */, 29512C7F1B91F86E008C10E6 /* api_test */, + 2940E9B8206605DE0054503C /* unit_test */, ); name = Products; sourceTree = ""; @@ -320,6 +365,23 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + 2940E98F206605DE0054503C /* unit_test */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2940E9B5206605DE0054503C /* Build configuration list for PBXNativeTarget "unit_test" */; + buildPhases = ( + 2940E990206605DE0054503C /* Sources */, + 2940E9B2206605DE0054503C /* Frameworks */, + 2940E9B4206605DE0054503C /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = unit_test; + productName = api_test; + productReference = 2940E9B8206605DE0054503C /* unit_test */; + productType = "com.apple.product-type.tool"; + }; 29AB1F051816E3AD004B501E /* wren */ = { isa = PBXNativeTarget; buildConfigurationList = 29AB1F0F1816E3AD004B501E /* Build configuration list for PBXNativeTarget "wren" */; @@ -382,11 +444,23 @@ targets = ( 29AB1F051816E3AD004B501E /* wren */, 29D0099E1B7E397D000CE58C /* api_test */, + 2940E98F206605DE0054503C /* unit_test */, ); }; /* End PBXProject section */ /* Begin PBXSourcesBuildPhase section */ + 2940E990206605DE0054503C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2940E9BC206607830054503C /* path.c in Sources */, + 2940E9BB2066067D0054503C /* path_test.c in Sources */, + 2940E9BE2066C3300054503C /* main.c in Sources */, + 2940E9C12066C35E0054503C /* test.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 29AB1F021816E3AD004B501E /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -395,6 +469,7 @@ 29205C991AB4E6430073018D /* wren_compiler.c in Sources */, 2986F6D71ACF93BA00BCE26C /* wren_primitive.c in Sources */, 291647C71BA5EC5E006142EE /* modules.c in Sources */, + 2952CD1D1FA9941700985F5F /* path.c in Sources */, 29A4273A1BDBE435001E6E22 /* wren_opt_random.wren.inc in Sources */, 29205C9A1AB4E6430073018D /* wren_core.c in Sources */, 2901D7641B74F4050083A2C8 /* timer.c in Sources */, @@ -460,6 +535,50 @@ /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ + 2940E9B6206605DE0054503C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_PEDANTIC = NO; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + LIBRARY_SEARCH_PATHS = ../../build; + MACOSX_DEPLOYMENT_TARGET = 10.10; + MTL_ENABLE_DEBUG_INFO = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + USER_HEADER_SEARCH_PATHS = "../../deps/libuv/include ../../src/module"; + }; + name = Debug; + }; + 2940E9B7206605DE0054503C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_PEDANTIC = NO; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + LIBRARY_SEARCH_PATHS = ../../build; + MACOSX_DEPLOYMENT_TARGET = 10.10; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_NAME = "$(TARGET_NAME)"; + USER_HEADER_SEARCH_PATHS = "../../deps/libuv/include ../../src/module"; + }; + name = Release; + }; 29AB1F0D1816E3AD004B501E /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -605,6 +724,15 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 2940E9B5206605DE0054503C /* Build configuration list for PBXNativeTarget "unit_test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2940E9B6206605DE0054503C /* Debug */, + 2940E9B7206605DE0054503C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 29AB1F011816E3AD004B501E /* Build configuration list for PBXProject "wren" */ = { isa = XCConfigurationList; buildConfigurations = (