Files
Odin/src/build_settings.cpp
gingerBill 458ec5922e odin query
Output .json file containing information about the program
2019-05-25 20:24:19 +01:00

634 lines
15 KiB
C++

enum TargetOsKind {
TargetOs_Invalid,
TargetOs_windows,
TargetOs_osx,
TargetOs_linux,
TargetOs_essence,
TargetOs_COUNT,
};
enum TargetArchKind {
TargetArch_Invalid,
TargetArch_amd64,
TargetArch_386,
TargetArch_COUNT,
};
enum TargetEndianKind {
TargetEndian_Invalid,
TargetEndian_Little,
TargetEndian_Big,
TargetEndian_COUNT,
};
String target_os_names[TargetOs_COUNT] = {
str_lit(""),
str_lit("windows"),
str_lit("osx"),
str_lit("linux"),
str_lit("essence"),
};
String target_arch_names[TargetArch_COUNT] = {
str_lit(""),
str_lit("amd64"),
str_lit("386"),
};
String target_endian_names[TargetEndian_COUNT] = {
str_lit(""),
str_lit("little"),
str_lit("big"),
};
TargetEndianKind target_endians[TargetArch_COUNT] = {
TargetEndian_Invalid,
TargetEndian_Little,
TargetEndian_Little,
};
String const ODIN_VERSION = str_lit("0.9.2");
String cross_compile_target = str_lit("");
String cross_compile_lib_dir = str_lit("");
struct TargetMetrics {
TargetOsKind os;
TargetArchKind arch;
isize word_size;
isize max_align;
};
// This stores the information for the specify architecture of this build
struct BuildContext {
// Constants
String ODIN_OS; // target operating system
String ODIN_ARCH; // target architecture
String ODIN_ENDIAN; // target endian
String ODIN_VENDOR; // compiler vendor
String ODIN_VERSION; // compiler version
String ODIN_ROOT; // Odin ROOT
bool ODIN_DEBUG; // Odin in debug mode
TargetEndianKind endian_kind;
// In bytes
i64 word_size; // Size of a pointer, must be >= 4
i64 max_align; // max alignment, must be >= 1 (and typically >= word_size)
String command;
TargetMetrics metrics;
String out_filepath;
String resource_filepath;
String pdb_filepath;
bool has_resource;
String opt_flags;
String llc_flags;
String link_flags;
bool is_dll;
bool generate_docs;
i32 optimization_level;
bool show_timings;
bool keep_temp_files;
bool ignore_unknown_attributes;
bool no_bounds_check;
bool no_output_files;
bool print_query_data;
bool no_crt;
bool use_lld;
bool vet;
gbAffinity affinity;
isize thread_count;
Map<ExactValue> defined_values; // Key:
};
gb_global BuildContext build_context = {0};
gb_global TargetMetrics target_windows_386 = {
TargetOs_windows,
TargetArch_386,
4,
8,
};
gb_global TargetMetrics target_windows_amd64 = {
TargetOs_windows,
TargetArch_amd64,
8,
16,
};
gb_global TargetMetrics target_linux_386 = {
TargetOs_linux,
TargetArch_386,
4,
8,
};
gb_global TargetMetrics target_linux_amd64 = {
TargetOs_linux,
TargetArch_amd64,
8,
16,
};
gb_global TargetMetrics target_osx_amd64 = {
TargetOs_osx,
TargetArch_amd64,
8,
16,
};
TargetOsKind get_target_os_from_string(String str) {
for (isize i = 0; i < TargetOs_COUNT; i++) {
if (str_eq_ignore_case(target_os_names[i], str)) {
return cast(TargetOsKind)i;
}
}
return TargetOs_Invalid;
}
TargetArchKind get_target_arch_from_string(String str) {
for (isize i = 0; i < TargetArch_COUNT; i++) {
if (str_eq_ignore_case(target_arch_names[i], str)) {
return cast(TargetArchKind)i;
}
}
return TargetArch_Invalid;
}
bool is_excluded_target_filename(String name) {
String const ext = str_lit(".odin");
String original_name = name;
GB_ASSERT(string_ends_with(name, ext));
name = substring(name, 0, name.len-ext.len);
String str1 = {};
String str2 = {};
isize n = 0;
str1 = name;
n = str1.len;
for (isize i = str1.len-1; i >= 0 && str1[i] != '_'; i--) {
n -= 1;
}
str1 = substring(str1, n, str1.len);
str2 = substring(name, 0, gb_max(n-1, 0));
n = str2.len;
for (isize i = str2.len-1; i >= 0 && str2[i] != '_'; i--) {
n -= 1;
}
str2 = substring(str2, n, str2.len);
if (str1 == name) {
return false;
}
TargetOsKind os1 = get_target_os_from_string(str1);
TargetArchKind arch1 = get_target_arch_from_string(str1);
TargetOsKind os2 = get_target_os_from_string(str2);
TargetArchKind arch2 = get_target_arch_from_string(str2);
if (os1 != TargetOs_Invalid && arch2 != TargetArch_Invalid) {
return os1 != build_context.metrics.os || arch2 != build_context.metrics.arch;
} else if (arch1 != TargetArch_Invalid && os2 != TargetOs_Invalid) {
return arch1 != build_context.metrics.arch || os2 != build_context.metrics.os;
} else if (os1 != TargetOs_Invalid) {
return os1 != build_context.metrics.os;
} else if (arch1 != TargetArch_Invalid) {
return arch1 != build_context.metrics.arch;
}
return false;
}
struct LibraryCollections {
String name;
String path;
};
gb_global Array<LibraryCollections> library_collections = {0};
void add_library_collection(String name, String path) {
// TODO(bill): Check the path is valid and a directory
LibraryCollections lc = {name, string_trim_whitespace(path)};
array_add(&library_collections, lc);
}
bool find_library_collection_path(String name, String *path) {
for_array(i, library_collections) {
if (library_collections[i].name == name) {
if (path) *path = library_collections[i].path;
return true;
}
}
return false;
}
// TODO(bill): OS dependent versions for the BuildContext
// join_path
// is_dir
// is_file
// is_abs_path
// has_subdir
String const WIN32_SEPARATOR_STRING = {cast(u8 *)"\\", 1};
String const NIX_SEPARATOR_STRING = {cast(u8 *)"/", 1};
#if defined(GB_SYSTEM_WINDOWS)
String odin_root_dir(void) {
String path = global_module_path;
isize len, i;
gbTempArenaMemory tmp;
wchar_t *text;
if (global_module_path_set) {
return global_module_path;
}
auto path_buf = array_make<wchar_t>(heap_allocator(), 300);
len = 0;
for (;;) {
len = GetModuleFileNameW(nullptr, &path_buf[0], cast(int)path_buf.count);
if (len == 0) {
return make_string(nullptr, 0);
}
if (len < path_buf.count) {
break;
}
array_resize(&path_buf, 2*path_buf.count + 300);
}
len += 1; // NOTE(bill): It needs an extra 1 for some reason
gb_mutex_lock(&string_buffer_mutex);
defer (gb_mutex_unlock(&string_buffer_mutex));
tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
defer (gb_temp_arena_memory_end(tmp));
text = gb_alloc_array(string_buffer_allocator, wchar_t, len+1);
GetModuleFileNameW(nullptr, text, cast(int)len);
path = string16_to_string(heap_allocator(), make_string16(text, len));
for (i = path.len-1; i >= 0; i--) {
u8 c = path[i];
if (c == '/' || c == '\\') {
break;
}
path.len--;
}
global_module_path = path;
global_module_path_set = true;
array_free(&path_buf);
return path;
}
#elif defined(GB_SYSTEM_OSX)
#include <mach-o/dyld.h>
String odin_root_dir(void) {
String path = global_module_path;
isize len, i;
gbTempArenaMemory tmp;
u8 *text;
if (global_module_path_set) {
return global_module_path;
}
auto path_buf = array_make<char>(heap_allocator(), 300);
len = 0;
for (;;) {
u32 sz = path_buf.count;
int res = _NSGetExecutablePath(&path_buf[0], &sz);
if(res == 0) {
len = sz;
break;
} else {
array_resize(&path_buf, sz + 1);
}
}
gb_mutex_lock(&string_buffer_mutex);
defer (gb_mutex_unlock(&string_buffer_mutex));
tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
defer (gb_temp_arena_memory_end(tmp));
text = gb_alloc_array(string_buffer_allocator, u8, len + 1);
gb_memmove(text, &path_buf[0], len);
path = make_string(text, len);
for (i = path.len-1; i >= 0; i--) {
u8 c = path[i];
if (c == '/' || c == '\\') {
break;
}
path.len--;
}
global_module_path = path;
global_module_path_set = true;
// array_free(&path_buf);
return path;
}
#else
// NOTE: Linux / Unix is unfinished and not tested very well.
#include <sys/stat.h>
String odin_root_dir(void) {
String path = global_module_path;
isize len, i;
gbTempArenaMemory tmp;
u8 *text;
if (global_module_path_set) {
return global_module_path;
}
auto path_buf = array_make<char>(heap_allocator(), 300);
defer (array_free(&path_buf));
len = 0;
for (;;) {
// This is not a 100% reliable system, but for the purposes
// of this compiler, it should be _good enough_.
// That said, there's no solid 100% method on Linux to get the program's
// path without checking this link. Sorry.
len = readlink("/proc/self/exe", &path_buf[0], path_buf.count);
if(len == 0) {
return make_string(nullptr, 0);
}
if (len < path_buf.count) {
break;
}
array_resize(&path_buf, 2*path_buf.count + 300);
}
gb_mutex_lock(&string_buffer_mutex);
defer (gb_mutex_unlock(&string_buffer_mutex));
tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
defer (gb_temp_arena_memory_end(tmp));
text = gb_alloc_array(string_buffer_allocator, u8, len + 1);
gb_memmove(text, &path_buf[0], len);
path = make_string(text, len);
for (i = path.len-1; i >= 0; i--) {
u8 c = path[i];
if (c == '/' || c == '\\') {
break;
}
path.len--;
}
global_module_path = path;
global_module_path_set = true;
return path;
}
#endif
#if defined(GB_SYSTEM_WINDOWS)
String path_to_fullpath(gbAllocator a, String s) {
String result = {};
gb_mutex_lock(&string_buffer_mutex);
defer (gb_mutex_unlock(&string_buffer_mutex));
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
defer (gb_temp_arena_memory_end(tmp));
String16 string16 = string_to_string16(string_buffer_allocator, s);
DWORD len = GetFullPathNameW(&string16[0], 0, nullptr, nullptr);
if (len != 0) {
wchar_t *text = gb_alloc_array(string_buffer_allocator, wchar_t, len+1);
GetFullPathNameW(&string16[0], len, text, nullptr);
text[len] = 0;
result = string16_to_string(a, make_string16(text, len));
result = string_trim_whitespace(result);
// Replace Windows style separators
for (isize i = 0; i < result.len; i++) {
if (result[i] == '\\') {
result[i] = '/';
}
}
}
return result;
}
#elif defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX)
String path_to_fullpath(gbAllocator a, String s) {
char *p;
gb_mutex_lock(&string_buffer_mutex);
p = realpath(cast(char *)s.text, 0);
gb_mutex_unlock(&string_buffer_mutex);
if(p == nullptr) return String{};
return make_string_c(p);
}
#else
#error Implement system
#endif
String get_fullpath_relative(gbAllocator a, String base_dir, String path) {
u8 *str = gb_alloc_array(heap_allocator(), u8, base_dir.len+1+path.len+1);
defer (gb_free(heap_allocator(), str));
isize i = 0;
gb_memmove(str+i, base_dir.text, base_dir.len); i += base_dir.len;
gb_memmove(str+i, "/", 1); i += 1;
gb_memmove(str+i, path.text, path.len); i += path.len;
str[i] = 0;
String res = make_string(str, i);
res = string_trim_whitespace(res);
return path_to_fullpath(a, res);
}
String get_fullpath_core(gbAllocator a, String path) {
String module_dir = odin_root_dir();
String core = str_lit("core/");
isize str_len = module_dir.len + core.len + path.len;
u8 *str = gb_alloc_array(heap_allocator(), u8, str_len+1);
defer (gb_free(heap_allocator(), str));
isize i = 0;
gb_memmove(str+i, module_dir.text, module_dir.len); i += module_dir.len;
gb_memmove(str+i, core.text, core.len); i += core.len;
gb_memmove(str+i, path.text, path.len); i += path.len;
str[i] = 0;
String res = make_string(str, i);
res = string_trim_whitespace(res);
return path_to_fullpath(a, res);
}
void init_build_context(void) {
BuildContext *bc = &build_context;
gb_affinity_init(&bc->affinity);
if (bc->thread_count == 0) {
bc->thread_count = gb_max(bc->affinity.thread_count, 1);
}
bc->ODIN_VENDOR = str_lit("odin");
bc->ODIN_VERSION = ODIN_VERSION;
bc->ODIN_ROOT = odin_root_dir();
TargetMetrics metrics = {};
#if defined(GB_ARCH_64_BIT)
#if defined(GB_SYSTEM_WINDOWS)
metrics = target_windows_amd64;
#elif defined(GB_SYSTEM_OSX)
metrics = target_osx_amd64;
#else
metrics = target_linux_amd64;
#endif
#else
#if defined(GB_SYSTEM_WINDOWS)
metrics = target_windows_386;
#elif defined(GB_SYSTEM_OSX)
#error "Unsupported architecture"
#else
metrics = target_linux_386;
#endif
#endif
if (cross_compile_target.len) {
bc->ODIN_OS = cross_compile_target;
}
GB_ASSERT(metrics.os != TargetOs_Invalid);
GB_ASSERT(metrics.arch != TargetArch_Invalid);
GB_ASSERT(metrics.word_size > 1);
GB_ASSERT(metrics.max_align > 1);
bc->metrics = metrics;
bc->ODIN_OS = target_os_names[metrics.os];
bc->ODIN_ARCH = target_arch_names[metrics.arch];
bc->ODIN_ENDIAN = target_endian_names[target_endians[metrics.arch]];
bc->endian_kind = target_endians[metrics.arch];
bc->word_size = metrics.word_size;
bc->max_align = metrics.max_align;
bc->link_flags = str_lit(" ");
bc->opt_flags = str_lit(" ");
gbString llc_flags = gb_string_make_reserve(heap_allocator(), 64);
if (bc->ODIN_DEBUG) {
// llc_flags = gb_string_appendc(llc_flags, "-debug-compile ");
}
// NOTE(zangent): The linker flags to set the build architecture are different
// across OSs. It doesn't make sense to allocate extra data on the heap
// here, so I just #defined the linker flags to keep things concise.
if (bc->metrics.arch == TargetArch_amd64) {
llc_flags = gb_string_appendc(llc_flags, "-march=x86-64 ");
switch (bc->metrics.os) {
case TargetOs_windows:
bc->link_flags = str_lit("/machine:x64 ");
break;
case TargetOs_osx:
break;
case TargetOs_linux:
bc->link_flags = str_lit("-arch x86-64 ");
break;
}
} else if (bc->metrics.arch == TargetArch_386) {
llc_flags = gb_string_appendc(llc_flags, "-march=x86 ");
switch (bc->metrics.os) {
case TargetOs_windows:
bc->link_flags = str_lit("/machine:x86 ");
break;
case TargetOs_osx:
gb_printf_err("Unsupported architecture\n");
gb_exit(1);
break;
case TargetOs_linux:
bc->link_flags = str_lit("-arch x86 ");
break;
}
} else {
gb_printf_err("Unsupported architecture\n");;
gb_exit(1);
}
bc->llc_flags = make_string_c(llc_flags);
bc->optimization_level = gb_clamp(bc->optimization_level, 0, 3);
gbString opt_flags = gb_string_make_reserve(heap_allocator(), 64);
if (bc->optimization_level != 0) {
opt_flags = gb_string_append_fmt(opt_flags, "-O%d ", bc->optimization_level);
// NOTE(lachsinc): The following options were previously passed during call
// to opt in main.cpp:exec_llvm_opt().
// -die: Dead instruction elimination
// -memcpyopt: MemCpy optimization
}
if (bc->ODIN_DEBUG == false) {
opt_flags = gb_string_appendc(opt_flags, "-memcpyopt -die ");
}
// NOTE(lachsinc): This optimization option was previously required to get
// around an issue in fmt.odin. Thank bp for tracking it down! Leaving for now until the issue
// is resolved and confirmed by Bill. Maybe it should be readded in non-debug builds.
// if (bc->ODIN_DEBUG == false) {
// opt_flags = gb_string_appendc(opt_flags, "-mem2reg ");
// }
bc->opt_flags = make_string_c(opt_flags);
#undef LINK_FLAG_X64
#undef LINK_FLAG_386
}