| author | |
| committer | |
| log | 173fc842c4eb1da6ca07a4ab6026c4c62bc8c09b |
| tree | a1bd31418545430c0c60fdebf00238d0034ae05d |
| parent | b4d5d4d1748d25efa6197914a36e276e93509f57 |
| signature | Commit is signed but in an unrecognized format. |
12 files changed, 918 insertions(+), 224 deletions(-)
CMakeLists.txt+1| ... | ... | @@ -409,6 +409,7 @@ set(ZIG_SOURCES |
| 409 | 409 | "${CMAKE_SOURCE_DIR}/src/bigint.cpp" |
| 410 | 410 | "${CMAKE_SOURCE_DIR}/src/blake2b.cpp" |
| 411 | 411 | "${CMAKE_SOURCE_DIR}/src/buffer.cpp" |
| 412 | "${CMAKE_SOURCE_DIR}/src/cache_hash.cpp" | |
| 412 | 413 | "${CMAKE_SOURCE_DIR}/src/c_tokenizer.cpp" |
| 413 | 414 | "${CMAKE_SOURCE_DIR}/src/codegen.cpp" |
| 414 | 415 | "${CMAKE_SOURCE_DIR}/src/errmsg.cpp" |
src/buffer.hpp+4| ... | ... | @@ -78,6 +78,10 @@ static inline Buf *buf_create_from_mem(const char *ptr, size_t len) { |
| 78 | 78 | return buf; |
| 79 | 79 | } |
| 80 | 80 | |
| 81 | static inline Buf *buf_create_from_slice(Slice<uint8_t> slice) { | |
| 82 | return buf_create_from_mem((const char *)slice.ptr, slice.len); | |
| 83 | } | |
| 84 | ||
| 81 | 85 | static inline Buf *buf_create_from_str(const char *str) { |
| 82 | 86 | return buf_create_from_mem(str, strlen(str)); |
| 83 | 87 | } |
src/cache_hash.cpp created+407| ... | ... | @@ -0,0 +1,407 @@ |
| 1 | /* | |
| 2 | * Copyright (c) 2018 Andrew Kelley | |
| 3 | * | |
| 4 | * This file is part of zig, which is MIT licensed. | |
| 5 | * See http://opensource.org/licenses/MIT | |
| 6 | */ | |
| 7 | ||
| 8 | #include "cache_hash.hpp" | |
| 9 | #include "buffer.hpp" | |
| 10 | #include "os.hpp" | |
| 11 | ||
| 12 | #include <stdio.h> | |
| 13 | ||
| 14 | void cache_init(CacheHash *ch, Buf *manifest_dir) { | |
| 15 | int rc = blake2b_init(&ch->blake, 48); | |
| 16 | assert(rc == 0); | |
| 17 | ch->files = {}; | |
| 18 | ch->manifest_dir = manifest_dir; | |
| 19 | ch->manifest_file_path = nullptr; | |
| 20 | ch->manifest_dirty = false; | |
| 21 | } | |
| 22 | ||
| 23 | void cache_str(CacheHash *ch, const char *ptr) { | |
| 24 | assert(ch->manifest_file_path == nullptr); | |
| 25 | assert(ptr != nullptr); | |
| 26 | // + 1 to include the null byte | |
| 27 | blake2b_update(&ch->blake, ptr, strlen(ptr) + 1); | |
| 28 | } | |
| 29 | ||
| 30 | void cache_int(CacheHash *ch, int x) { | |
| 31 | assert(ch->manifest_file_path == nullptr); | |
| 32 | // + 1 to include the null byte | |
| 33 | uint8_t buf[sizeof(int) + 1]; | |
| 34 | memcpy(buf, &x, sizeof(int)); | |
| 35 | buf[sizeof(int)] = 0; | |
| 36 | blake2b_update(&ch->blake, buf, sizeof(int) + 1); | |
| 37 | } | |
| 38 | ||
| 39 | void cache_buf(CacheHash *ch, Buf *buf) { | |
| 40 | assert(ch->manifest_file_path == nullptr); | |
| 41 | assert(buf != nullptr); | |
| 42 | // + 1 to include the null byte | |
| 43 | blake2b_update(&ch->blake, buf_ptr(buf), buf_len(buf) + 1); | |
| 44 | } | |
| 45 | ||
| 46 | void cache_buf_opt(CacheHash *ch, Buf *buf) { | |
| 47 | assert(ch->manifest_file_path == nullptr); | |
| 48 | if (buf == nullptr) { | |
| 49 | cache_str(ch, ""); | |
| 50 | cache_str(ch, ""); | |
| 51 | } else { | |
| 52 | cache_buf(ch, buf); | |
| 53 | } | |
| 54 | } | |
| 55 | ||
| 56 | void cache_list_of_link_lib(CacheHash *ch, LinkLib **ptr, size_t len) { | |
| 57 | assert(ch->manifest_file_path == nullptr); | |
| 58 | for (size_t i = 0; i < len; i += 1) { | |
| 59 | LinkLib *lib = ptr[i]; | |
| 60 | if (lib->provided_explicitly) { | |
| 61 | cache_buf(ch, lib->name); | |
| 62 | } | |
| 63 | } | |
| 64 | cache_str(ch, ""); | |
| 65 | } | |
| 66 | ||
| 67 | void cache_list_of_buf(CacheHash *ch, Buf **ptr, size_t len) { | |
| 68 | assert(ch->manifest_file_path == nullptr); | |
| 69 | for (size_t i = 0; i < len; i += 1) { | |
| 70 | Buf *buf = ptr[i]; | |
| 71 | cache_buf(ch, buf); | |
| 72 | } | |
| 73 | cache_str(ch, ""); | |
| 74 | } | |
| 75 | ||
| 76 | void cache_file(CacheHash *ch, Buf *file_path) { | |
| 77 | assert(ch->manifest_file_path == nullptr); | |
| 78 | assert(file_path != nullptr); | |
| 79 | Buf *resolved_path = buf_alloc(); | |
| 80 | *resolved_path = os_path_resolve(&file_path, 1); | |
| 81 | CacheHashFile *chf = ch->files.add_one(); | |
| 82 | chf->path = resolved_path; | |
| 83 | cache_buf(ch, resolved_path); | |
| 84 | } | |
| 85 | ||
| 86 | void cache_file_opt(CacheHash *ch, Buf *file_path) { | |
| 87 | assert(ch->manifest_file_path == nullptr); | |
| 88 | if (file_path == nullptr) { | |
| 89 | cache_str(ch, ""); | |
| 90 | cache_str(ch, ""); | |
| 91 | } else { | |
| 92 | cache_file(ch, file_path); | |
| 93 | } | |
| 94 | } | |
| 95 | ||
| 96 | // Ported from std/base64.zig | |
| 97 | static uint8_t base64_fs_alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; | |
| 98 | static void base64_encode(Slice<uint8_t> dest, Slice<uint8_t> source) { | |
| 99 | size_t dest_len = ((source.len + 2) / 3) * 4; | |
| 100 | assert(dest.len == dest_len); | |
| 101 | ||
| 102 | size_t i = 0; | |
| 103 | size_t out_index = 0; | |
| 104 | for (; i + 2 < source.len; i += 3) { | |
| 105 | dest.ptr[out_index] = base64_fs_alphabet[(source.ptr[i] >> 2) & 0x3f]; | |
| 106 | out_index += 1; | |
| 107 | ||
| 108 | dest.ptr[out_index] = base64_fs_alphabet[((source.ptr[i] & 0x3) << 4) | ((source.ptr[i + 1] & 0xf0) >> 4)]; | |
| 109 | out_index += 1; | |
| 110 | ||
| 111 | dest.ptr[out_index] = base64_fs_alphabet[((source.ptr[i + 1] & 0xf) << 2) | ((source.ptr[i + 2] & 0xc0) >> 6)]; | |
| 112 | out_index += 1; | |
| 113 | ||
| 114 | dest.ptr[out_index] = base64_fs_alphabet[source.ptr[i + 2] & 0x3f]; | |
| 115 | out_index += 1; | |
| 116 | } | |
| 117 | ||
| 118 | // Assert that we never need pad characters. | |
| 119 | assert(i == source.len); | |
| 120 | } | |
| 121 | ||
| 122 | // Ported from std/base64.zig | |
| 123 | static Error base64_decode(Slice<uint8_t> dest, Slice<uint8_t> source) { | |
| 124 | assert(source.len % 4 == 0); | |
| 125 | assert(dest.len == (source.len / 4) * 3); | |
| 126 | ||
| 127 | // In Zig this is comptime computed. In C++ it's not worth it to do that. | |
| 128 | uint8_t char_to_index[256]; | |
| 129 | bool char_in_alphabet[256] = {0}; | |
| 130 | for (size_t i = 0; i < 64; i += 1) { | |
| 131 | uint8_t c = base64_fs_alphabet[i]; | |
| 132 | assert(!char_in_alphabet[c]); | |
| 133 | char_in_alphabet[c] = true; | |
| 134 | char_to_index[c] = i; | |
| 135 | } | |
| 136 | ||
| 137 | size_t src_cursor = 0; | |
| 138 | size_t dest_cursor = 0; | |
| 139 | ||
| 140 | for (;src_cursor < source.len; src_cursor += 4) { | |
| 141 | if (!char_in_alphabet[source.ptr[src_cursor + 0]]) return ErrorInvalidFormat; | |
| 142 | if (!char_in_alphabet[source.ptr[src_cursor + 1]]) return ErrorInvalidFormat; | |
| 143 | if (!char_in_alphabet[source.ptr[src_cursor + 2]]) return ErrorInvalidFormat; | |
| 144 | if (!char_in_alphabet[source.ptr[src_cursor + 3]]) return ErrorInvalidFormat; | |
| 145 | dest.ptr[dest_cursor + 0] = (char_to_index[source.ptr[src_cursor + 0]] << 2) | (char_to_index[source.ptr[src_cursor + 1]] >> 4); | |
| 146 | dest.ptr[dest_cursor + 1] = (char_to_index[source.ptr[src_cursor + 1]] << 4) | (char_to_index[source.ptr[src_cursor + 2]] >> 2); | |
| 147 | dest.ptr[dest_cursor + 2] = (char_to_index[source.ptr[src_cursor + 2]] << 6) | (char_to_index[source.ptr[src_cursor + 3]]); | |
| 148 | dest_cursor += 3; | |
| 149 | } | |
| 150 | ||
| 151 | assert(src_cursor == source.len); | |
| 152 | assert(dest_cursor == dest.len); | |
| 153 | return ErrorNone; | |
| 154 | } | |
| 155 | ||
| 156 | static Error hash_file(uint8_t *digest, OsFile handle) { | |
| 157 | Error err; | |
| 158 | ||
| 159 | blake2b_state blake; | |
| 160 | int rc = blake2b_init(&blake, 48); | |
| 161 | assert(rc == 0); | |
| 162 | ||
| 163 | for (;;) { | |
| 164 | uint8_t buf[4096]; | |
| 165 | size_t amt = 4096; | |
| 166 | if ((err = os_file_read(handle, buf, &amt))) | |
| 167 | return err; | |
| 168 | if (amt == 0) { | |
| 169 | rc = blake2b_final(&blake, digest, 48); | |
| 170 | assert(rc == 0); | |
| 171 | return ErrorNone; | |
| 172 | } | |
| 173 | blake2b_update(&blake, buf, amt); | |
| 174 | } | |
| 175 | } | |
| 176 | ||
| 177 | static Error populate_file_hash(CacheHash *ch, CacheHashFile *chf) { | |
| 178 | Error err; | |
| 179 | ||
| 180 | assert(chf->path != nullptr); | |
| 181 | ||
| 182 | OsFile this_file; | |
| 183 | if ((err = os_file_open_r(chf->path, &this_file))) | |
| 184 | return err; | |
| 185 | ||
| 186 | if ((err = os_file_mtime(this_file, &chf->mtime))) { | |
| 187 | os_file_close(this_file); | |
| 188 | return err; | |
| 189 | } | |
| 190 | ||
| 191 | if ((err = hash_file(chf->bin_digest, this_file))) { | |
| 192 | os_file_close(this_file); | |
| 193 | return err; | |
| 194 | } | |
| 195 | os_file_close(this_file); | |
| 196 | ||
| 197 | blake2b_update(&ch->blake, chf->bin_digest, 48); | |
| 198 | ||
| 199 | return ErrorNone; | |
| 200 | } | |
| 201 | ||
| 202 | Error cache_hit(CacheHash *ch, Buf *out_digest) { | |
| 203 | Error err; | |
| 204 | ||
| 205 | uint8_t bin_digest[48]; | |
| 206 | int rc = blake2b_final(&ch->blake, bin_digest, 48); | |
| 207 | assert(rc == 0); | |
| 208 | ||
| 209 | Buf b64_digest = BUF_INIT; | |
| 210 | buf_resize(&b64_digest, 64); | |
| 211 | base64_encode(buf_to_slice(&b64_digest), {bin_digest, 48}); | |
| 212 | ||
| 213 | rc = blake2b_init(&ch->blake, 48); | |
| 214 | assert(rc == 0); | |
| 215 | blake2b_update(&ch->blake, bin_digest, 48); | |
| 216 | ||
| 217 | ch->manifest_file_path = buf_alloc(); | |
| 218 | os_path_join(ch->manifest_dir, &b64_digest, ch->manifest_file_path); | |
| 219 | ||
| 220 | buf_append_str(ch->manifest_file_path, ".txt"); | |
| 221 | ||
| 222 | if ((err = os_file_open_lock_rw(ch->manifest_file_path, &ch->manifest_file))) | |
| 223 | return err; | |
| 224 | ||
| 225 | Buf line_buf = BUF_INIT; | |
| 226 | buf_resize(&line_buf, 512); | |
| 227 | if ((err = os_file_read_all(ch->manifest_file, &line_buf))) { | |
| 228 | os_file_close(ch->manifest_file); | |
| 229 | return err; | |
| 230 | } | |
| 231 | ||
| 232 | size_t input_file_count = ch->files.length; | |
| 233 | bool any_file_changed = false; | |
| 234 | size_t file_i = 0; | |
| 235 | SplitIterator line_it = memSplit(buf_to_slice(&line_buf), str("\n")); | |
| 236 | for (;; file_i += 1) { | |
| 237 | Optional<Slice<uint8_t>> opt_line = SplitIterator_next(&line_it); | |
| 238 | if (!opt_line.is_some) | |
| 239 | break; | |
| 240 | ||
| 241 | CacheHashFile *chf; | |
| 242 | if (file_i < input_file_count) { | |
| 243 | chf = &ch->files.at(file_i); | |
| 244 | } else if (any_file_changed) { | |
| 245 | // cache miss. | |
| 246 | // keep the the manifest file open with the rw lock | |
| 247 | // reset the hash | |
| 248 | rc = blake2b_init(&ch->blake, 48); | |
| 249 | assert(rc == 0); | |
| 250 | blake2b_update(&ch->blake, bin_digest, 48); | |
| 251 | ch->files.resize(input_file_count); | |
| 252 | // bring the hash up to the input file hashes | |
| 253 | for (file_i = 0; file_i < input_file_count; file_i += 1) { | |
| 254 | blake2b_update(&ch->blake, ch->files.at(file_i).bin_digest, 48); | |
| 255 | } | |
| 256 | // caller can notice that out_digest is unmodified. | |
| 257 | return ErrorNone; | |
| 258 | } else { | |
| 259 | chf = ch->files.add_one(); | |
| 260 | chf->path = nullptr; | |
| 261 | } | |
| 262 | ||
| 263 | SplitIterator it = memSplit(opt_line.value, str(" ")); | |
| 264 | ||
| 265 | Optional<Slice<uint8_t>> opt_mtime_sec = SplitIterator_next(&it); | |
| 266 | if (!opt_mtime_sec.is_some) { | |
| 267 | os_file_close(ch->manifest_file); | |
| 268 | return ErrorInvalidFormat; | |
| 269 | } | |
| 270 | chf->mtime.sec = strtoull((const char *)opt_mtime_sec.value.ptr, nullptr, 10); | |
| 271 | ||
| 272 | Optional<Slice<uint8_t>> opt_mtime_nsec = SplitIterator_next(&it); | |
| 273 | if (!opt_mtime_nsec.is_some) { | |
| 274 | os_file_close(ch->manifest_file); | |
| 275 | return ErrorInvalidFormat; | |
| 276 | } | |
| 277 | chf->mtime.nsec = strtoull((const char *)opt_mtime_nsec.value.ptr, nullptr, 10); | |
| 278 | ||
| 279 | Optional<Slice<uint8_t>> opt_digest = SplitIterator_next(&it); | |
| 280 | if (!opt_digest.is_some) { | |
| 281 | os_file_close(ch->manifest_file); | |
| 282 | return ErrorInvalidFormat; | |
| 283 | } | |
| 284 | if ((err = base64_decode({chf->bin_digest, 48}, opt_digest.value))) { | |
| 285 | os_file_close(ch->manifest_file); | |
| 286 | return ErrorInvalidFormat; | |
| 287 | } | |
| 288 | ||
| 289 | Optional<Slice<uint8_t>> opt_file_path = SplitIterator_next(&it); | |
| 290 | if (!opt_file_path.is_some) { | |
| 291 | os_file_close(ch->manifest_file); | |
| 292 | return ErrorInvalidFormat; | |
| 293 | } | |
| 294 | Buf *this_path = buf_create_from_slice(opt_file_path.value); | |
| 295 | if (chf->path != nullptr && !buf_eql_buf(this_path, chf->path)) { | |
| 296 | os_file_close(ch->manifest_file); | |
| 297 | return ErrorInvalidFormat; | |
| 298 | } | |
| 299 | chf->path = this_path; | |
| 300 | ||
| 301 | // if the mtime matches we can trust the digest | |
| 302 | OsFile this_file; | |
| 303 | if ((err = os_file_open_r(chf->path, &this_file))) { | |
| 304 | os_file_close(ch->manifest_file); | |
| 305 | return err; | |
| 306 | } | |
| 307 | OsTimeStamp actual_mtime; | |
| 308 | if ((err = os_file_mtime(this_file, &actual_mtime))) { | |
| 309 | os_file_close(this_file); | |
| 310 | os_file_close(ch->manifest_file); | |
| 311 | return err; | |
| 312 | } | |
| 313 | if (chf->mtime.sec == actual_mtime.sec && chf->mtime.nsec == actual_mtime.nsec) { | |
| 314 | os_file_close(this_file); | |
| 315 | } else { | |
| 316 | // we have to recompute the digest. | |
| 317 | // later we'll rewrite the manifest with the new mtime/digest values | |
| 318 | ch->manifest_dirty = true; | |
| 319 | chf->mtime = actual_mtime; | |
| 320 | ||
| 321 | uint8_t actual_digest[48]; | |
| 322 | if ((err = hash_file(actual_digest, this_file))) { | |
| 323 | os_file_close(this_file); | |
| 324 | os_file_close(ch->manifest_file); | |
| 325 | return err; | |
| 326 | } | |
| 327 | os_file_close(this_file); | |
| 328 | if (memcmp(chf->bin_digest, actual_digest, 48) != 0) { | |
| 329 | memcpy(chf->bin_digest, actual_digest, 48); | |
| 330 | // keep going until we have the input file digests | |
| 331 | any_file_changed = true; | |
| 332 | } | |
| 333 | } | |
| 334 | if (!any_file_changed) { | |
| 335 | blake2b_update(&ch->blake, chf->bin_digest, 48); | |
| 336 | } | |
| 337 | } | |
| 338 | if (file_i < input_file_count) { | |
| 339 | // manifest file is empty or missing entries, so this is a cache miss | |
| 340 | ch->manifest_dirty = true; | |
| 341 | for (; file_i < input_file_count; file_i += 1) { | |
| 342 | CacheHashFile *chf = &ch->files.at(file_i); | |
| 343 | if ((err = populate_file_hash(ch, chf))) { | |
| 344 | os_file_close(ch->manifest_file); | |
| 345 | return err; | |
| 346 | } | |
| 347 | } | |
| 348 | return ErrorNone; | |
| 349 | } | |
| 350 | // Cache Hit | |
| 351 | return cache_final(ch, out_digest); | |
| 352 | } | |
| 353 | ||
| 354 | Error cache_add_file(CacheHash *ch, Buf *path) { | |
| 355 | Error err; | |
| 356 | ||
| 357 | assert(ch->manifest_file_path != nullptr); | |
| 358 | CacheHashFile *chf = ch->files.add_one(); | |
| 359 | chf->path = path; | |
| 360 | if ((err = populate_file_hash(ch, chf))) { | |
| 361 | os_file_close(ch->manifest_file); | |
| 362 | return err; | |
| 363 | } | |
| 364 | ||
| 365 | return ErrorNone; | |
| 366 | } | |
| 367 | ||
| 368 | static Error write_manifest_file(CacheHash *ch) { | |
| 369 | Error err; | |
| 370 | Buf contents = BUF_INIT; | |
| 371 | buf_resize(&contents, 0); | |
| 372 | uint8_t encoded_digest[65]; | |
| 373 | encoded_digest[64] = 0; | |
| 374 | for (size_t i = 0; i < ch->files.length; i += 1) { | |
| 375 | CacheHashFile *chf = &ch->files.at(i); | |
| 376 | base64_encode({encoded_digest, 64}, {chf->bin_digest, 48}); | |
| 377 | buf_appendf(&contents, "%" ZIG_PRI_u64 " %" ZIG_PRI_u64 " %s %s\n", | |
| 378 | chf->mtime.sec, chf->mtime.nsec, encoded_digest, buf_ptr(chf->path)); | |
| 379 | } | |
| 380 | fprintf(stderr, "overwrite with\n%s\n", buf_ptr(&contents)); | |
| 381 | if ((err = os_file_overwrite(ch->manifest_file, &contents))) | |
| 382 | return err; | |
| 383 | ||
| 384 | return ErrorNone; | |
| 385 | } | |
| 386 | ||
| 387 | Error cache_final(CacheHash *ch, Buf *out_digest) { | |
| 388 | Error err; | |
| 389 | ||
| 390 | assert(ch->manifest_file_path != nullptr); | |
| 391 | ||
| 392 | if (ch->manifest_dirty) { | |
| 393 | if ((err = write_manifest_file(ch))) { | |
| 394 | fprintf(stderr, "Warning: Unable to write cache file '%s': %s\n", | |
| 395 | buf_ptr(ch->manifest_file_path), err_str(err)); | |
| 396 | } | |
| 397 | } | |
| 398 | os_file_close(ch->manifest_file); | |
| 399 | ||
| 400 | uint8_t bin_digest[48]; | |
| 401 | int rc = blake2b_final(&ch->blake, bin_digest, 48); | |
| 402 | assert(rc == 0); | |
| 403 | buf_resize(out_digest, 64); | |
| 404 | base64_encode(buf_to_slice(out_digest), {bin_digest, 48}); | |
| 405 | ||
| 406 | return ErrorNone; | |
| 407 | } |
src/cache_hash.hpp created+56| ... | ... | @@ -0,0 +1,56 @@ |
| 1 | /* | |
| 2 | * Copyright (c) 2018 Andrew Kelley | |
| 3 | * | |
| 4 | * This file is part of zig, which is MIT licensed. | |
| 5 | * See http://opensource.org/licenses/MIT | |
| 6 | */ | |
| 7 | ||
| 8 | #ifndef ZIG_CACHE_HASH_HPP | |
| 9 | #define ZIG_CACHE_HASH_HPP | |
| 10 | ||
| 11 | #include "all_types.hpp" | |
| 12 | #include "blake2.h" | |
| 13 | #include "os.hpp" | |
| 14 | ||
| 15 | struct CacheHashFile { | |
| 16 | Buf *path; | |
| 17 | OsTimeStamp mtime; | |
| 18 | uint8_t bin_digest[48]; | |
| 19 | }; | |
| 20 | ||
| 21 | struct CacheHash { | |
| 22 | blake2b_state blake; | |
| 23 | ZigList<CacheHashFile> files; | |
| 24 | Buf *manifest_dir; | |
| 25 | Buf *manifest_file_path; | |
| 26 | OsFile manifest_file; | |
| 27 | bool manifest_dirty; | |
| 28 | }; | |
| 29 | ||
| 30 | // Always call this first to set up. | |
| 31 | void cache_init(CacheHash *ch, Buf *manifest_dir); | |
| 32 | ||
| 33 | // Next, use the hash population functions to add the initial parameters. | |
| 34 | void cache_str(CacheHash *ch, const char *ptr); | |
| 35 | void cache_int(CacheHash *ch, int x); | |
| 36 | void cache_buf(CacheHash *ch, Buf *buf); | |
| 37 | void cache_buf_opt(CacheHash *ch, Buf *buf); | |
| 38 | void cache_list_of_link_lib(CacheHash *ch, LinkLib **ptr, size_t len); | |
| 39 | void cache_list_of_buf(CacheHash *ch, Buf **ptr, size_t len); | |
| 40 | void cache_file(CacheHash *ch, Buf *path); | |
| 41 | void cache_file_opt(CacheHash *ch, Buf *path); | |
| 42 | ||
| 43 | // Then call cache_hit when you're ready to see if you can skip the next step. | |
| 44 | // out_b64_digest will be left unchanged if it was a cache miss | |
| 45 | Error ATTRIBUTE_MUST_USE cache_hit(CacheHash *ch, Buf *out_b64_digest); | |
| 46 | ||
| 47 | // If you got a cache hit, the flow is done. No more functions to call. | |
| 48 | // Next call this function for every file that is depended on. | |
| 49 | Error ATTRIBUTE_MUST_USE cache_add_file(CacheHash *ch, Buf *path); | |
| 50 | ||
| 51 | // If you did not get a cache hit, use the hash population functions again | |
| 52 | // and do all the actual work. When done use cache_final to save the cache | |
| 53 | // for next time. | |
| 54 | Error ATTRIBUTE_MUST_USE cache_final(CacheHash *ch, Buf *out_digest); | |
| 55 | ||
| 56 | #endif |
src/codegen.cpp+33-119| ... | ... | @@ -19,7 +19,6 @@ |
| 19 | 19 | #include "target.hpp" |
| 20 | 20 | #include "util.hpp" |
| 21 | 21 | #include "zig_llvm.h" |
| 22 | #include "blake2.h" | |
| 23 | 22 | |
| 24 | 23 | #include <stdio.h> |
| 25 | 24 | #include <errno.h> |
| ... | ... | @@ -7675,130 +7674,45 @@ void codegen_add_time_event(CodeGen *g, const char *name) { |
| 7675 | 7674 | g->timing_events.append({os_get_time(), name}); |
| 7676 | 7675 | } |
| 7677 | 7676 | |
| 7678 | static void add_cache_str(blake2b_state *blake, const char *ptr) { | |
| 7679 | assert(ptr != nullptr); | |
| 7680 | // + 1 to include the null byte | |
| 7681 | blake2b_update(blake, ptr, strlen(ptr) + 1); | |
| 7682 | } | |
| 7683 | ||
| 7684 | static void add_cache_int(blake2b_state *blake, int x) { | |
| 7685 | // + 1 to include the null byte | |
| 7686 | uint8_t buf[sizeof(int) + 1]; | |
| 7687 | memcpy(buf, &x, sizeof(int)); | |
| 7688 | buf[sizeof(int)] = 0; | |
| 7689 | blake2b_update(blake, buf, sizeof(int) + 1); | |
| 7690 | } | |
| 7691 | 7677 | |
| 7692 | static void add_cache_buf(blake2b_state *blake, Buf *buf) { | |
| 7693 | assert(buf != nullptr); | |
| 7694 | // + 1 to include the null byte | |
| 7695 | blake2b_update(blake, buf_ptr(buf), buf_len(buf) + 1); | |
| 7696 | } | |
| 7697 | ||
| 7698 | static void add_cache_buf_opt(blake2b_state *blake, Buf *buf) { | |
| 7699 | if (buf == nullptr) { | |
| 7700 | add_cache_str(blake, ""); | |
| 7701 | add_cache_str(blake, ""); | |
| 7702 | } else { | |
| 7703 | add_cache_buf(blake, buf); | |
| 7704 | } | |
| 7705 | } | |
| 7706 | ||
| 7707 | static void add_cache_list_of_link_lib(blake2b_state *blake, LinkLib **ptr, size_t len) { | |
| 7708 | for (size_t i = 0; i < len; i += 1) { | |
| 7709 | LinkLib *lib = ptr[i]; | |
| 7710 | if (lib->provided_explicitly) { | |
| 7711 | add_cache_buf(blake, lib->name); | |
| 7712 | } | |
| 7713 | } | |
| 7714 | add_cache_str(blake, ""); | |
| 7715 | } | |
| 7716 | ||
| 7717 | static void add_cache_list_of_buf(blake2b_state *blake, Buf **ptr, size_t len) { | |
| 7718 | for (size_t i = 0; i < len; i += 1) { | |
| 7719 | Buf *buf = ptr[i]; | |
| 7720 | add_cache_buf(blake, buf); | |
| 7721 | } | |
| 7722 | add_cache_str(blake, ""); | |
| 7723 | } | |
| 7724 | ||
| 7725 | //static void add_cache_file(CodeGen *g, blake2b_state *blake, Buf *resolved_path) { | |
| 7726 | // assert(file_name != nullptr); | |
| 7727 | // g->cache_files.append(resolved_path); | |
| 7728 | //} | |
| 7678 | //// Called before init() | |
| 7679 | //static bool build_with_cache(CodeGen *g) { | |
| 7680 | // // TODO zig exe & dynamic libraries | |
| 7681 | // // should be in main.cpp I think. only needs to happen | |
| 7682 | // // once on startup. | |
| 7683 | // | |
| 7684 | // CacheHash comp; | |
| 7685 | // cache_init(&comp); | |
| 7686 | // | |
| 7687 | // add_cache_buf(&blake, g->root_out_name); | |
| 7688 | // add_cache_buf_opt(&blake, get_resolved_root_src_path(g)); // Root source file | |
| 7689 | // add_cache_list_of_link_lib(&blake, g->link_libs_list.items, g->link_libs_list.length); | |
| 7690 | // add_cache_list_of_buf(&blake, g->darwin_frameworks.items, g->darwin_frameworks.length); | |
| 7691 | // add_cache_list_of_buf(&blake, g->rpath_list.items, g->rpath_list.length); | |
| 7692 | // add_cache_int(&blake, g->emit_file_type); | |
| 7693 | // add_cache_int(&blake, g->build_mode); | |
| 7694 | // add_cache_int(&blake, g->out_type); | |
| 7695 | // // TODO the rest of the struct CodeGen fields | |
| 7696 | // | |
| 7697 | // uint8_t bin_digest[48]; | |
| 7698 | // rc = blake2b_final(&blake, bin_digest, 48); | |
| 7699 | // assert(rc == 0); | |
| 7700 | // | |
| 7701 | // Buf b64_digest = BUF_INIT; | |
| 7702 | // buf_resize(&b64_digest, 64); | |
| 7703 | // base64_encode(buf_to_slice(&b64_digest), {bin_digest, 48}); | |
| 7729 | 7704 | // |
| 7730 | //static void add_cache_file_opt(CodeGen *g, blake2b_state *blake, Buf *resolved_path) { | |
| 7731 | // if (resolved_path == nullptr) { | |
| 7732 | // add_cache_str(blake, ""); | |
| 7733 | // add_cache_str(blake, ""); | |
| 7734 | // } else { | |
| 7735 | // add_cache_file(g, blake, resolved_path); | |
| 7736 | // } | |
| 7705 | // fprintf(stderr, "input params hash: %s\n", buf_ptr(&b64_digest)); | |
| 7706 | // // TODO next look for a manifest file that has all the files from the input parameters | |
| 7707 | // // use that to construct the real hash, which looks up the output directory and another manifest file | |
| 7708 | // | |
| 7709 | // return false; | |
| 7737 | 7710 | //} |
| 7738 | 7711 | |
| 7739 | // Ported from std/base64.zig | |
| 7740 | static uint8_t base64_fs_alphabet[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"; | |
| 7741 | static void base64_encode(Slice<uint8_t> dest, Slice<uint8_t> source) { | |
| 7742 | size_t dest_len = ((source.len + 2) / 3) * 4; | |
| 7743 | assert(dest.len == dest_len); | |
| 7744 | ||
| 7745 | size_t i = 0; | |
| 7746 | size_t out_index = 0; | |
| 7747 | for (; i + 2 < source.len; i += 3) { | |
| 7748 | dest.ptr[out_index] = base64_fs_alphabet[(source.ptr[i] >> 2) & 0x3f]; | |
| 7749 | out_index += 1; | |
| 7750 | ||
| 7751 | dest.ptr[out_index] = base64_fs_alphabet[((source.ptr[i] & 0x3) << 4) | ((source.ptr[i + 1] & 0xf0) >> 4)]; | |
| 7752 | out_index += 1; | |
| 7753 | ||
| 7754 | dest.ptr[out_index] = base64_fs_alphabet[((source.ptr[i + 1] & 0xf) << 2) | ((source.ptr[i + 2] & 0xc0) >> 6)]; | |
| 7755 | out_index += 1; | |
| 7756 | ||
| 7757 | dest.ptr[out_index] = base64_fs_alphabet[source.ptr[i + 2] & 0x3f]; | |
| 7758 | out_index += 1; | |
| 7759 | } | |
| 7760 | ||
| 7761 | // Assert that we never need pad characters. | |
| 7762 | assert(i == source.len); | |
| 7763 | } | |
| 7764 | ||
| 7765 | // Called before init() | |
| 7766 | static bool build_with_cache(CodeGen *g) { | |
| 7767 | blake2b_state blake; | |
| 7768 | int rc = blake2b_init(&blake, 48); | |
| 7769 | assert(rc == 0); | |
| 7770 | ||
| 7771 | // TODO zig exe & dynamic libraries | |
| 7772 | ||
| 7773 | add_cache_buf(&blake, g->root_out_name); | |
| 7774 | add_cache_buf_opt(&blake, get_resolved_root_src_path(g)); // Root source file | |
| 7775 | add_cache_list_of_link_lib(&blake, g->link_libs_list.items, g->link_libs_list.length); | |
| 7776 | add_cache_list_of_buf(&blake, g->darwin_frameworks.items, g->darwin_frameworks.length); | |
| 7777 | add_cache_list_of_buf(&blake, g->rpath_list.items, g->rpath_list.length); | |
| 7778 | add_cache_int(&blake, g->emit_file_type); | |
| 7779 | add_cache_int(&blake, g->build_mode); | |
| 7780 | add_cache_int(&blake, g->out_type); | |
| 7781 | // TODO the rest of the struct CodeGen fields | |
| 7782 | ||
| 7783 | uint8_t bin_digest[48]; | |
| 7784 | rc = blake2b_final(&blake, bin_digest, 48); | |
| 7785 | assert(rc == 0); | |
| 7786 | ||
| 7787 | Buf b64_digest = BUF_INIT; | |
| 7788 | buf_resize(&b64_digest, 64); | |
| 7789 | base64_encode(buf_to_slice(&b64_digest), {bin_digest, 48}); | |
| 7790 | ||
| 7791 | fprintf(stderr, "input params hash: %s\n", buf_ptr(&b64_digest)); | |
| 7792 | // TODO next look for a manifest file that has all the files from the input parameters | |
| 7793 | // use that to construct the real hash, which looks up the output directory and another manifest file | |
| 7794 | ||
| 7795 | return false; | |
| 7796 | } | |
| 7797 | ||
| 7798 | 7712 | void codegen_build(CodeGen *g) { |
| 7799 | 7713 | assert(g->out_type != OutTypeUnknown); |
| 7800 | if (build_with_cache(g)) | |
| 7801 | return; | |
| 7714 | //if (build_with_cache(g)) | |
| 7715 | // return; | |
| 7802 | 7716 | init(g); |
| 7803 | 7717 | |
| 7804 | 7718 | codegen_add_time_event(g, "Semantic Analysis"); |
src/error.cpp+2| ... | ... | @@ -27,6 +27,8 @@ const char *err_str(int err) { |
| 27 | 27 | case ErrorNegativeDenominator: return "negative denominator"; |
| 28 | 28 | case ErrorShiftedOutOneBits: return "exact shift shifted out one bits"; |
| 29 | 29 | case ErrorCCompileErrors: return "C compile errors"; |
| 30 | case ErrorEndOfFile: return "end of file"; | |
| 31 | case ErrorIsDir: return "is directory"; | |
| 30 | 32 | } |
| 31 | 33 | return "(invalid error)"; |
| 32 | 34 | } |
src/error.hpp+2| ... | ... | @@ -27,6 +27,8 @@ enum Error { |
| 27 | 27 | ErrorNegativeDenominator, |
| 28 | 28 | ErrorShiftedOutOneBits, |
| 29 | 29 | ErrorCCompileErrors, |
| 30 | ErrorEndOfFile, | |
| 31 | ErrorIsDir, | |
| 30 | 32 | }; |
| 31 | 33 | |
| 32 | 34 | const char *err_str(int err); |
src/main.cpp+61| ... | ... | @@ -13,6 +13,7 @@ |
| 13 | 13 | #include "link.hpp" |
| 14 | 14 | #include "os.hpp" |
| 15 | 15 | #include "target.hpp" |
| 16 | #include "cache_hash.hpp" | |
| 16 | 17 | |
| 17 | 18 | #include <stdio.h> |
| 18 | 19 | |
| ... | ... | @@ -270,6 +271,66 @@ int main(int argc, char **argv) { |
| 270 | 271 | return 0; |
| 271 | 272 | } |
| 272 | 273 | |
| 274 | if (argc == 2 && strcmp(argv[1], "TEST") == 0) { | |
| 275 | Error err; | |
| 276 | Buf app_data_dir = BUF_INIT; | |
| 277 | if ((err = os_get_app_data_dir(&app_data_dir, "zig"))) { | |
| 278 | fprintf(stderr, "get app dir: %s\n", err_str(err)); | |
| 279 | return 1; | |
| 280 | } | |
| 281 | Buf *stage1_dir = buf_alloc(); | |
| 282 | os_path_join(&app_data_dir, buf_create_from_str("stage1"), stage1_dir); | |
| 283 | Buf *manifest_dir = buf_alloc(); | |
| 284 | os_path_join(stage1_dir, buf_create_from_str("exe"), manifest_dir); | |
| 285 | ||
| 286 | if ((err = os_make_path(manifest_dir))) { | |
| 287 | fprintf(stderr, "make path: %s\n", err_str(err)); | |
| 288 | return 1; | |
| 289 | } | |
| 290 | CacheHash cache_hash; | |
| 291 | CacheHash *ch = &cache_hash; | |
| 292 | cache_init(ch, manifest_dir); | |
| 293 | Buf self_exe_path = BUF_INIT; | |
| 294 | if ((err = os_self_exe_path(&self_exe_path))) { | |
| 295 | fprintf(stderr, "self exe path: %s\n", err_str(err)); | |
| 296 | return 1; | |
| 297 | } | |
| 298 | ||
| 299 | cache_file(ch, &self_exe_path); | |
| 300 | ||
| 301 | Buf exe_digest = BUF_INIT; | |
| 302 | buf_resize(&exe_digest, 0); | |
| 303 | if ((err = cache_hit(ch, &exe_digest))) { | |
| 304 | fprintf(stderr, "cache hit error: %s\n", err_str(err)); | |
| 305 | return 1; | |
| 306 | } | |
| 307 | if (buf_len(&exe_digest) != 0) { | |
| 308 | fprintf(stderr, "cache hit: %s\n", buf_ptr(&exe_digest)); | |
| 309 | return 0; | |
| 310 | } | |
| 311 | fprintf(stderr, "cache miss\n"); | |
| 312 | ZigList<Buf *> lib_paths = {}; | |
| 313 | if ((err = os_self_exe_shared_libs(lib_paths))) { | |
| 314 | fprintf(stderr, "finding out shared libs: %s\n", err_str(err)); | |
| 315 | return 1; | |
| 316 | } | |
| 317 | for (size_t i = 0; i < lib_paths.length; i += 1) { | |
| 318 | Buf *lib_path = lib_paths.at(i); | |
| 319 | if ((err = cache_add_file(ch, lib_path))) { | |
| 320 | fprintf(stderr, "cache add file %s: %s", buf_ptr(lib_path), err_str(err)); | |
| 321 | return 1; | |
| 322 | } | |
| 323 | } | |
| 324 | if ((err = cache_final(ch, &exe_digest))) { | |
| 325 | fprintf(stderr, "final: %s\n", err_str(err)); | |
| 326 | return 1; | |
| 327 | } | |
| 328 | ||
| 329 | ||
| 330 | fprintf(stderr, "computed2: %s\n", buf_ptr(&exe_digest)); | |
| 331 | return 0; | |
| 332 | } | |
| 333 | ||
| 273 | 334 | os_init(); |
| 274 | 335 | |
| 275 | 336 | char *arg0 = argv[0]; |
src/os.cpp+243-71| ... | ... | @@ -40,6 +40,10 @@ typedef SSIZE_T ssize_t; |
| 40 | 40 | |
| 41 | 41 | #endif |
| 42 | 42 | |
| 43 | #if defined(ZIG_OS_LINUX) | |
| 44 | #include <link.h> | |
| 45 | #endif | |
| 46 | ||
| 43 | 47 | |
| 44 | 48 | #if defined(__MACH__) |
| 45 | 49 | #include <mach/clock.h> |
| ... | ... | @@ -57,54 +61,6 @@ static clock_serv_t cclock; |
| 57 | 61 | #include <errno.h> |
| 58 | 62 | #include <time.h> |
| 59 | 63 | |
| 60 | // Ported from std/mem.zig. | |
| 61 | // Coordinate struct fields with memSplit function | |
| 62 | struct SplitIterator { | |
| 63 | size_t index; | |
| 64 | Slice<uint8_t> buffer; | |
| 65 | Slice<uint8_t> split_bytes; | |
| 66 | }; | |
| 67 | ||
| 68 | // Ported from std/mem.zig. | |
| 69 | static bool SplitIterator_isSplitByte(SplitIterator *self, uint8_t byte) { | |
| 70 | for (size_t i = 0; i < self->split_bytes.len; i += 1) { | |
| 71 | if (byte == self->split_bytes.ptr[i]) { | |
| 72 | return true; | |
| 73 | } | |
| 74 | } | |
| 75 | return false; | |
| 76 | } | |
| 77 | ||
| 78 | // Ported from std/mem.zig. | |
| 79 | static Optional<Slice<uint8_t>> SplitIterator_next(SplitIterator *self) { | |
| 80 | // move to beginning of token | |
| 81 | while (self->index < self->buffer.len && | |
| 82 | SplitIterator_isSplitByte(self, self->buffer.ptr[self->index])) | |
| 83 | { | |
| 84 | self->index += 1; | |
| 85 | } | |
| 86 | size_t start = self->index; | |
| 87 | if (start == self->buffer.len) { | |
| 88 | return {}; | |
| 89 | } | |
| 90 | ||
| 91 | // move to end of token | |
| 92 | while (self->index < self->buffer.len && | |
| 93 | !SplitIterator_isSplitByte(self, self->buffer.ptr[self->index])) | |
| 94 | { | |
| 95 | self->index += 1; | |
| 96 | } | |
| 97 | size_t end = self->index; | |
| 98 | ||
| 99 | return Optional<Slice<uint8_t>>::some(self->buffer.slice(start, end)); | |
| 100 | } | |
| 101 | ||
| 102 | // Ported from std/mem.zig | |
| 103 | static SplitIterator memSplit(Slice<uint8_t> buffer, Slice<uint8_t> split_bytes) { | |
| 104 | return SplitIterator{0, buffer, split_bytes}; | |
| 105 | } | |
| 106 | ||
| 107 | ||
| 108 | 64 | #if defined(ZIG_OS_POSIX) |
| 109 | 65 | static void populate_termination(Termination *term, int status) { |
| 110 | 66 | if (WIFEXITED(status)) { |
| ... | ... | @@ -1368,16 +1324,16 @@ double os_get_time(void) { |
| 1368 | 1324 | #endif |
| 1369 | 1325 | } |
| 1370 | 1326 | |
| 1371 | int os_make_path(Buf *path) { | |
| 1327 | Error os_make_path(Buf *path) { | |
| 1372 | 1328 | Buf resolved_path = os_path_resolve(&path, 1); |
| 1373 | 1329 | |
| 1374 | 1330 | size_t end_index = buf_len(&resolved_path); |
| 1375 | int err; | |
| 1331 | Error err; | |
| 1376 | 1332 | while (true) { |
| 1377 | 1333 | if ((err = os_make_dir(buf_slice(&resolved_path, 0, end_index)))) { |
| 1378 | 1334 | if (err == ErrorPathAlreadyExists) { |
| 1379 | 1335 | if (end_index == buf_len(&resolved_path)) |
| 1380 | return 0; | |
| 1336 | return ErrorNone; | |
| 1381 | 1337 | } else if (err == ErrorFileNotFound) { |
| 1382 | 1338 | // march end_index backward until next path component |
| 1383 | 1339 | while (true) { |
| ... | ... | @@ -1391,7 +1347,7 @@ int os_make_path(Buf *path) { |
| 1391 | 1347 | } |
| 1392 | 1348 | } |
| 1393 | 1349 | if (end_index == buf_len(&resolved_path)) |
| 1394 | return 0; | |
| 1350 | return ErrorNone; | |
| 1395 | 1351 | // march end_index forward until next path component |
| 1396 | 1352 | while (true) { |
| 1397 | 1353 | end_index += 1; |
| ... | ... | @@ -1399,10 +1355,10 @@ int os_make_path(Buf *path) { |
| 1399 | 1355 | break; |
| 1400 | 1356 | } |
| 1401 | 1357 | } |
| 1402 | return 0; | |
| 1358 | return ErrorNone; | |
| 1403 | 1359 | } |
| 1404 | 1360 | |
| 1405 | int os_make_dir(Buf *path) { | |
| 1361 | Error os_make_dir(Buf *path) { | |
| 1406 | 1362 | #if defined(ZIG_OS_WINDOWS) |
| 1407 | 1363 | if (!CreateDirectory(buf_ptr(path), NULL)) { |
| 1408 | 1364 | if (GetLastError() == ERROR_ALREADY_EXISTS) |
| ... | ... | @@ -1413,7 +1369,7 @@ int os_make_dir(Buf *path) { |
| 1413 | 1369 | return ErrorAccess; |
| 1414 | 1370 | return ErrorUnexpected; |
| 1415 | 1371 | } |
| 1416 | return 0; | |
| 1372 | return ErrorNone; | |
| 1417 | 1373 | #else |
| 1418 | 1374 | if (mkdir(buf_ptr(path), 0755) == -1) { |
| 1419 | 1375 | if (errno == EEXIST) |
| ... | ... | @@ -1424,7 +1380,7 @@ int os_make_dir(Buf *path) { |
| 1424 | 1380 | return ErrorAccess; |
| 1425 | 1381 | return ErrorUnexpected; |
| 1426 | 1382 | } |
| 1427 | return 0; | |
| 1383 | return ErrorNone; | |
| 1428 | 1384 | #endif |
| 1429 | 1385 | } |
| 1430 | 1386 | |
| ... | ... | @@ -1447,7 +1403,7 @@ int os_init(void) { |
| 1447 | 1403 | return 0; |
| 1448 | 1404 | } |
| 1449 | 1405 | |
| 1450 | int os_self_exe_path(Buf *out_path) { | |
| 1406 | Error os_self_exe_path(Buf *out_path) { | |
| 1451 | 1407 | #if defined(ZIG_OS_WINDOWS) |
| 1452 | 1408 | buf_resize(out_path, 256); |
| 1453 | 1409 | for (;;) { |
| ... | ... | @@ -1480,27 +1436,21 @@ int os_self_exe_path(Buf *out_path) { |
| 1480 | 1436 | char *real_path = realpath(buf_ptr(tmp), buf_ptr(out_path)); |
| 1481 | 1437 | if (!real_path) { |
| 1482 | 1438 | buf_init_from_buf(out_path, tmp); |
| 1483 | return 0; | |
| 1439 | return ErrorNone; | |
| 1484 | 1440 | } |
| 1485 | 1441 | |
| 1486 | 1442 | // Resize out_path for the correct length. |
| 1487 | 1443 | buf_resize(out_path, strlen(buf_ptr(out_path))); |
| 1488 | 1444 | |
| 1489 | return 0; | |
| 1445 | return ErrorNone; | |
| 1490 | 1446 | #elif defined(ZIG_OS_LINUX) |
| 1491 | buf_resize(out_path, 256); | |
| 1492 | for (;;) { | |
| 1493 | ssize_t amt = readlink("/proc/self/exe", buf_ptr(out_path), buf_len(out_path)); | |
| 1494 | if (amt == -1) { | |
| 1495 | return ErrorUnexpected; | |
| 1496 | } | |
| 1497 | if (amt == (ssize_t)buf_len(out_path)) { | |
| 1498 | buf_resize(out_path, buf_len(out_path) * 2); | |
| 1499 | continue; | |
| 1500 | } | |
| 1501 | buf_resize(out_path, amt); | |
| 1502 | return 0; | |
| 1447 | buf_resize(out_path, PATH_MAX); | |
| 1448 | ssize_t amt = readlink("/proc/self/exe", buf_ptr(out_path), buf_len(out_path)); | |
| 1449 | if (amt == -1) { | |
| 1450 | return ErrorUnexpected; | |
| 1503 | 1451 | } |
| 1452 | buf_resize(out_path, amt); | |
| 1453 | return ErrorNone; | |
| 1504 | 1454 | #endif |
| 1505 | 1455 | return ErrorFileNotFound; |
| 1506 | 1456 | } |
| ... | ... | @@ -1685,3 +1635,225 @@ int os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchTy |
| 1685 | 1635 | return ErrorFileNotFound; |
| 1686 | 1636 | #endif |
| 1687 | 1637 | } |
| 1638 | ||
| 1639 | // Ported from std/os/get_app_data_dir.zig | |
| 1640 | Error os_get_app_data_dir(Buf *out_path, const char *appname) { | |
| 1641 | #if defined(ZIG_OS_WINDOWS) | |
| 1642 | #error "Unimplemented" | |
| 1643 | #elif defined(ZIG_OS_DARWIN) | |
| 1644 | const char *home_dir = getenv("HOME"); | |
| 1645 | if (home_dir == nullptr) { | |
| 1646 | // TODO use /etc/passwd | |
| 1647 | return ErrorFileNotFound; | |
| 1648 | } | |
| 1649 | buf_resize(out_path, 0); | |
| 1650 | buf_appendf(out_path, "%s/Library/Application Support/%s", home_dir, appname); | |
| 1651 | return ErrorNone; | |
| 1652 | #elif defined(ZIG_OS_LINUX) | |
| 1653 | const char *home_dir = getenv("HOME"); | |
| 1654 | if (home_dir == nullptr) { | |
| 1655 | // TODO use /etc/passwd | |
| 1656 | return ErrorFileNotFound; | |
| 1657 | } | |
| 1658 | buf_resize(out_path, 0); | |
| 1659 | buf_appendf(out_path, "%s/.local/share/%s", home_dir, appname); | |
| 1660 | return ErrorNone; | |
| 1661 | #endif | |
| 1662 | } | |
| 1663 | ||
| 1664 | ||
| 1665 | #if defined(ZIG_OS_LINUX) | |
| 1666 | static int self_exe_shared_libs_callback(struct dl_phdr_info *info, size_t size, void *data) { | |
| 1667 | ZigList<Buf *> *libs = reinterpret_cast< ZigList<Buf *> *>(data); | |
| 1668 | if (info->dlpi_name[0] == '/') { | |
| 1669 | libs->append(buf_create_from_str(info->dlpi_name)); | |
| 1670 | } | |
| 1671 | return 0; | |
| 1672 | } | |
| 1673 | #endif | |
| 1674 | ||
| 1675 | Error os_self_exe_shared_libs(ZigList<Buf *> &paths) { | |
| 1676 | #if defined(ZIG_OS_LINUX) | |
| 1677 | paths.resize(0); | |
| 1678 | dl_iterate_phdr(self_exe_shared_libs_callback, &paths); | |
| 1679 | return ErrorNone; | |
| 1680 | #else | |
| 1681 | #error "unimplemented" | |
| 1682 | #endif | |
| 1683 | } | |
| 1684 | ||
| 1685 | Error os_file_open_r(Buf *full_path, OsFile *out_file) { | |
| 1686 | #if defined(ZIG_OS_WINDOWS) | |
| 1687 | #error "unimplemented" | |
| 1688 | #else | |
| 1689 | for (;;) { | |
| 1690 | int fd = open(buf_ptr(full_path), O_RDONLY|O_CLOEXEC); | |
| 1691 | if (fd == -1) { | |
| 1692 | switch (errno) { | |
| 1693 | case EINTR: | |
| 1694 | continue; | |
| 1695 | case EINVAL: | |
| 1696 | zig_unreachable(); | |
| 1697 | case EFAULT: | |
| 1698 | zig_unreachable(); | |
| 1699 | case EACCES: | |
| 1700 | return ErrorAccess; | |
| 1701 | case EISDIR: | |
| 1702 | return ErrorIsDir; | |
| 1703 | case ENOENT: | |
| 1704 | return ErrorFileNotFound; | |
| 1705 | default: | |
| 1706 | return ErrorFileSystem; | |
| 1707 | } | |
| 1708 | } | |
| 1709 | *out_file = fd; | |
| 1710 | return ErrorNone; | |
| 1711 | } | |
| 1712 | #endif | |
| 1713 | } | |
| 1714 | ||
| 1715 | Error os_file_open_lock_rw(Buf *full_path, OsFile *out_file) { | |
| 1716 | #if defined(ZIG_OS_WINDOWS) | |
| 1717 | #error "unimplemented" | |
| 1718 | #else | |
| 1719 | int fd; | |
| 1720 | for (;;) { | |
| 1721 | fd = open(buf_ptr(full_path), O_RDWR|O_CLOEXEC|O_CREAT, 0666); | |
| 1722 | if (fd == -1) { | |
| 1723 | switch (errno) { | |
| 1724 | case EINTR: | |
| 1725 | continue; | |
| 1726 | case EINVAL: | |
| 1727 | zig_unreachable(); | |
| 1728 | case EFAULT: | |
| 1729 | zig_unreachable(); | |
| 1730 | case EACCES: | |
| 1731 | return ErrorAccess; | |
| 1732 | case EISDIR: | |
| 1733 | return ErrorIsDir; | |
| 1734 | case ENOENT: | |
| 1735 | return ErrorFileNotFound; | |
| 1736 | default: | |
| 1737 | return ErrorFileSystem; | |
| 1738 | } | |
| 1739 | } | |
| 1740 | break; | |
| 1741 | } | |
| 1742 | for (;;) { | |
| 1743 | struct flock lock; | |
| 1744 | lock.l_type = F_WRLCK; | |
| 1745 | lock.l_whence = SEEK_SET; | |
| 1746 | lock.l_start = 0; | |
| 1747 | lock.l_len = 0; | |
| 1748 | if (fcntl(fd, F_SETLKW, &lock) == -1) { | |
| 1749 | switch (errno) { | |
| 1750 | case EINTR: | |
| 1751 | continue; | |
| 1752 | case EBADF: | |
| 1753 | zig_unreachable(); | |
| 1754 | case EFAULT: | |
| 1755 | zig_unreachable(); | |
| 1756 | case EINVAL: | |
| 1757 | zig_unreachable(); | |
| 1758 | default: | |
| 1759 | close(fd); | |
| 1760 | return ErrorFileSystem; | |
| 1761 | } | |
| 1762 | } | |
| 1763 | break; | |
| 1764 | } | |
| 1765 | *out_file = fd; | |
| 1766 | return ErrorNone; | |
| 1767 | #endif | |
| 1768 | } | |
| 1769 | ||
| 1770 | Error os_file_mtime(OsFile file, OsTimeStamp *mtime) { | |
| 1771 | #if defined(ZIG_OS_WINDOWS) | |
| 1772 | #error unimplemented | |
| 1773 | #else | |
| 1774 | struct stat statbuf; | |
| 1775 | if (fstat(file, &statbuf) == -1) | |
| 1776 | return ErrorFileSystem; | |
| 1777 | ||
| 1778 | mtime->sec = statbuf.st_mtim.tv_sec; | |
| 1779 | mtime->nsec = statbuf.st_mtim.tv_nsec; | |
| 1780 | return ErrorNone; | |
| 1781 | #endif | |
| 1782 | } | |
| 1783 | ||
| 1784 | Error os_file_read(OsFile file, void *ptr, size_t *len) { | |
| 1785 | #if defined(ZIG_OS_WINDOWS) | |
| 1786 | #error unimplemented | |
| 1787 | #else | |
| 1788 | for (;;) { | |
| 1789 | ssize_t rc = read(file, ptr, *len); | |
| 1790 | if (rc == -1) { | |
| 1791 | switch (errno) { | |
| 1792 | case EINTR: | |
| 1793 | continue; | |
| 1794 | case EBADF: | |
| 1795 | zig_unreachable(); | |
| 1796 | case EFAULT: | |
| 1797 | zig_unreachable(); | |
| 1798 | case EISDIR: | |
| 1799 | zig_unreachable(); | |
| 1800 | default: | |
| 1801 | return ErrorFileSystem; | |
| 1802 | } | |
| 1803 | } | |
| 1804 | *len = rc; | |
| 1805 | return ErrorNone; | |
| 1806 | } | |
| 1807 | #endif | |
| 1808 | } | |
| 1809 | ||
| 1810 | Error os_file_read_all(OsFile file, Buf *contents) { | |
| 1811 | Error err; | |
| 1812 | size_t index = 0; | |
| 1813 | for (;;) { | |
| 1814 | size_t amt = buf_len(contents) - index; | |
| 1815 | ||
| 1816 | if (amt < 512) { | |
| 1817 | buf_resize(contents, buf_len(contents) + 512); | |
| 1818 | amt += 512; | |
| 1819 | } | |
| 1820 | ||
| 1821 | if ((err = os_file_read(file, buf_ptr(contents) + index, &amt))) | |
| 1822 | return err; | |
| 1823 | ||
| 1824 | if (amt == 0) { | |
| 1825 | buf_resize(contents, index); | |
| 1826 | return ErrorNone; | |
| 1827 | } | |
| 1828 | ||
| 1829 | index += amt; | |
| 1830 | } | |
| 1831 | } | |
| 1832 | ||
| 1833 | Error os_file_overwrite(OsFile file, Buf *contents) { | |
| 1834 | #if defined(ZIG_OS_WINDOWS) | |
| 1835 | #error unimplemented | |
| 1836 | #else | |
| 1837 | if (lseek(file, 0, SEEK_SET) == -1) | |
| 1838 | return ErrorFileSystem; | |
| 1839 | for (;;) { | |
| 1840 | if (write(file, buf_ptr(contents), buf_len(contents)) == -1) { | |
| 1841 | switch (errno) { | |
| 1842 | case EINTR: | |
| 1843 | continue; | |
| 1844 | case EINVAL: | |
| 1845 | zig_unreachable(); | |
| 1846 | case EBADF: | |
| 1847 | zig_unreachable(); | |
| 1848 | default: | |
| 1849 | return ErrorFileSystem; | |
| 1850 | } | |
| 1851 | } | |
| 1852 | return ErrorNone; | |
| 1853 | } | |
| 1854 | #endif | |
| 1855 | } | |
| 1856 | ||
| 1857 | void os_file_close(OsFile file) { | |
| 1858 | close(file); | |
| 1859 | } |
src/os.hpp+58-34| ... | ... | @@ -13,10 +13,43 @@ |
| 13 | 13 | #include "error.hpp" |
| 14 | 14 | #include "zig_llvm.h" |
| 15 | 15 | #include "windows_sdk.h" |
| 16 | #include "result.hpp" | |
| 16 | 17 | |
| 17 | 18 | #include <stdio.h> |
| 18 | 19 | #include <inttypes.h> |
| 19 | 20 | |
| 21 | #if defined(__APPLE__) | |
| 22 | #define ZIG_OS_DARWIN | |
| 23 | #elif defined(_WIN32) | |
| 24 | #define ZIG_OS_WINDOWS | |
| 25 | #elif defined(__linux__) | |
| 26 | #define ZIG_OS_LINUX | |
| 27 | #else | |
| 28 | #define ZIG_OS_UNKNOWN | |
| 29 | #endif | |
| 30 | ||
| 31 | #if defined(__x86_64__) | |
| 32 | #define ZIG_ARCH_X86_64 | |
| 33 | #else | |
| 34 | #define ZIG_ARCH_UNKNOWN | |
| 35 | #endif | |
| 36 | ||
| 37 | #if defined(ZIG_OS_WINDOWS) | |
| 38 | #define ZIG_PRI_usize "I64u" | |
| 39 | #define ZIG_PRI_u64 "I64u" | |
| 40 | #define ZIG_PRI_llu "I64u" | |
| 41 | #define ZIG_PRI_x64 "I64x" | |
| 42 | #define OS_SEP "\\" | |
| 43 | #define ZIG_OS_SEP_CHAR '\\' | |
| 44 | #else | |
| 45 | #define ZIG_PRI_usize "zu" | |
| 46 | #define ZIG_PRI_u64 PRIu64 | |
| 47 | #define ZIG_PRI_llu "llu" | |
| 48 | #define ZIG_PRI_x64 PRIx64 | |
| 49 | #define OS_SEP "/" | |
| 50 | #define ZIG_OS_SEP_CHAR '/' | |
| 51 | #endif | |
| 52 | ||
| 20 | 53 | enum TermColor { |
| 21 | 54 | TermColorRed, |
| 22 | 55 | TermColorGreen, |
| ... | ... | @@ -38,6 +71,17 @@ struct Termination { |
| 38 | 71 | int code; |
| 39 | 72 | }; |
| 40 | 73 | |
| 74 | #if defined(ZIG_OS_WINDOWS) | |
| 75 | #define OsFile (void *) | |
| 76 | #else | |
| 77 | #define OsFile int | |
| 78 | #endif | |
| 79 | ||
| 80 | struct OsTimeStamp { | |
| 81 | uint64_t sec; | |
| 82 | uint64_t nsec; | |
| 83 | }; | |
| 84 | ||
| 41 | 85 | int os_init(void); |
| 42 | 86 | |
| 43 | 87 | void os_spawn_process(const char *exe, ZigList<const char *> &args, Termination *term); |
| ... | ... | @@ -54,8 +98,16 @@ bool os_path_is_absolute(Buf *path); |
| 54 | 98 | |
| 55 | 99 | int os_get_global_cache_directory(Buf *out_tmp_path); |
| 56 | 100 | |
| 57 | int os_make_path(Buf *path); | |
| 58 | int os_make_dir(Buf *path); | |
| 101 | Error ATTRIBUTE_MUST_USE os_make_path(Buf *path); | |
| 102 | Error ATTRIBUTE_MUST_USE os_make_dir(Buf *path); | |
| 103 | ||
| 104 | Error ATTRIBUTE_MUST_USE os_file_open_r(Buf *full_path, OsFile *out_file); | |
| 105 | Error ATTRIBUTE_MUST_USE os_file_open_lock_rw(Buf *full_path, OsFile *out_file); | |
| 106 | Error ATTRIBUTE_MUST_USE os_file_mtime(OsFile file, OsTimeStamp *mtime); | |
| 107 | Error ATTRIBUTE_MUST_USE os_file_read(OsFile file, void *ptr, size_t *len); | |
| 108 | Error ATTRIBUTE_MUST_USE os_file_read_all(OsFile file, Buf *contents); | |
| 109 | Error ATTRIBUTE_MUST_USE os_file_overwrite(OsFile file, Buf *contents); | |
| 110 | void os_file_close(OsFile file); | |
| 59 | 111 | |
| 60 | 112 | void os_write_file(Buf *full_path, Buf *contents); |
| 61 | 113 | int os_copy_file(Buf *src_path, Buf *dest_path); |
| ... | ... | @@ -78,42 +130,14 @@ double os_get_time(void); |
| 78 | 130 | |
| 79 | 131 | bool os_is_sep(uint8_t c); |
| 80 | 132 | |
| 81 | int os_self_exe_path(Buf *out_path); | |
| 133 | Error ATTRIBUTE_MUST_USE os_self_exe_path(Buf *out_path); | |
| 134 | ||
| 135 | Error ATTRIBUTE_MUST_USE os_get_app_data_dir(Buf *out_path, const char *appname); | |
| 82 | 136 | |
| 83 | 137 | int os_get_win32_ucrt_include_path(ZigWindowsSDK *sdk, Buf *output_buf); |
| 84 | 138 | int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf *output_buf, ZigLLVM_ArchType platform_type); |
| 85 | 139 | int os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf *output_buf, ZigLLVM_ArchType platform_type); |
| 86 | 140 | |
| 87 | #if defined(__APPLE__) | |
| 88 | #define ZIG_OS_DARWIN | |
| 89 | #elif defined(_WIN32) | |
| 90 | #define ZIG_OS_WINDOWS | |
| 91 | #elif defined(__linux__) | |
| 92 | #define ZIG_OS_LINUX | |
| 93 | #else | |
| 94 | #define ZIG_OS_UNKNOWN | |
| 95 | #endif | |
| 96 | ||
| 97 | #if defined(__x86_64__) | |
| 98 | #define ZIG_ARCH_X86_64 | |
| 99 | #else | |
| 100 | #define ZIG_ARCH_UNKNOWN | |
| 101 | #endif | |
| 102 | ||
| 103 | #if defined(ZIG_OS_WINDOWS) | |
| 104 | #define ZIG_PRI_usize "I64u" | |
| 105 | #define ZIG_PRI_u64 "I64u" | |
| 106 | #define ZIG_PRI_llu "I64u" | |
| 107 | #define ZIG_PRI_x64 "I64x" | |
| 108 | #define OS_SEP "\\" | |
| 109 | #define ZIG_OS_SEP_CHAR '\\' | |
| 110 | #else | |
| 111 | #define ZIG_PRI_usize "zu" | |
| 112 | #define ZIG_PRI_u64 PRIu64 | |
| 113 | #define ZIG_PRI_llu "llu" | |
| 114 | #define ZIG_PRI_x64 PRIx64 | |
| 115 | #define OS_SEP "/" | |
| 116 | #define ZIG_OS_SEP_CHAR '/' | |
| 117 | #endif | |
| 141 | Error ATTRIBUTE_MUST_USE os_self_exe_shared_libs(ZigList<Buf *> &paths); | |
| 118 | 142 | |
| 119 | 143 | #endif |
src/util.cpp+39| ... | ... | @@ -43,3 +43,42 @@ uint32_t ptr_hash(const void *ptr) { |
| 43 | 43 | bool ptr_eq(const void *a, const void *b) { |
| 44 | 44 | return a == b; |
| 45 | 45 | } |
| 46 | ||
| 47 | // Ported from std/mem.zig. | |
| 48 | bool SplitIterator_isSplitByte(SplitIterator *self, uint8_t byte) { | |
| 49 | for (size_t i = 0; i < self->split_bytes.len; i += 1) { | |
| 50 | if (byte == self->split_bytes.ptr[i]) { | |
| 51 | return true; | |
| 52 | } | |
| 53 | } | |
| 54 | return false; | |
| 55 | } | |
| 56 | ||
| 57 | // Ported from std/mem.zig. | |
| 58 | Optional<Slice<uint8_t>> SplitIterator_next(SplitIterator *self) { | |
| 59 | // move to beginning of token | |
| 60 | while (self->index < self->buffer.len && | |
| 61 | SplitIterator_isSplitByte(self, self->buffer.ptr[self->index])) | |
| 62 | { | |
| 63 | self->index += 1; | |
| 64 | } | |
| 65 | size_t start = self->index; | |
| 66 | if (start == self->buffer.len) { | |
| 67 | return {}; | |
| 68 | } | |
| 69 | ||
| 70 | // move to end of token | |
| 71 | while (self->index < self->buffer.len && | |
| 72 | !SplitIterator_isSplitByte(self, self->buffer.ptr[self->index])) | |
| 73 | { | |
| 74 | self->index += 1; | |
| 75 | } | |
| 76 | size_t end = self->index; | |
| 77 | ||
| 78 | return Optional<Slice<uint8_t>>::some(self->buffer.slice(start, end)); | |
| 79 | } | |
| 80 | ||
| 81 | // Ported from std/mem.zig | |
| 82 | SplitIterator memSplit(Slice<uint8_t> buffer, Slice<uint8_t> split_bytes) { | |
| 83 | return SplitIterator{0, buffer, split_bytes}; | |
| 84 | } |
src/util.hpp+12| ... | ... | @@ -254,4 +254,16 @@ static inline void memCopy(Slice<T> dest, Slice<T> src) { |
| 254 | 254 | memcpy(dest.ptr, src.ptr, src.len * sizeof(T)); |
| 255 | 255 | } |
| 256 | 256 | |
| 257 | // Ported from std/mem.zig. | |
| 258 | // Coordinate struct fields with memSplit function | |
| 259 | struct SplitIterator { | |
| 260 | size_t index; | |
| 261 | Slice<uint8_t> buffer; | |
| 262 | Slice<uint8_t> split_bytes; | |
| 263 | }; | |
| 264 | ||
| 265 | bool SplitIterator_isSplitByte(SplitIterator *self, uint8_t byte); | |
| 266 | Optional<Slice<uint8_t>> SplitIterator_next(SplitIterator *self); | |
| 267 | SplitIterator memSplit(Slice<uint8_t> buffer, Slice<uint8_t> split_bytes); | |
| 268 | ||
| 257 | 269 | #endif |