data_compiler.cpp 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194
  1. /*
  2. * Copyright (c) 2012-2020 Daniele Bartolini and individual contributors.
  3. * License: https://github.com/dbartolini/crown/blob/master/LICENSE
  4. */
  5. #include "config.h"
  6. #if CROWN_CAN_COMPILE
  7. #include "core/containers/hash_map.h"
  8. #include "core/containers/hash_set.h"
  9. #include "core/containers/vector.h"
  10. #include "core/filesystem/file.h"
  11. #include "core/filesystem/filesystem_disk.h"
  12. #include "core/filesystem/path.h"
  13. #include "core/json/json_object.h"
  14. #include "core/json/sjson.h"
  15. #include "core/memory/allocator.h"
  16. #include "core/memory/temp_allocator.h"
  17. #include "core/os.h"
  18. #include "core/strings/dynamic_string.h"
  19. #include "core/strings/string_stream.h"
  20. #include "core/time.h"
  21. #include "device/console_server.h"
  22. #include "device/device_options.h"
  23. #include "device/log.h"
  24. #include "resource/compile_options.h"
  25. #include "resource/config_resource.h"
  26. #include "resource/data_compiler.h"
  27. #include "resource/font_resource.h"
  28. #include "resource/level_resource.h"
  29. #include "resource/lua_resource.h"
  30. #include "resource/material_resource.h"
  31. #include "resource/mesh_resource.h"
  32. #include "resource/package_resource.h"
  33. #include "resource/physics_resource.h"
  34. #include "resource/resource_id.h"
  35. #include "resource/shader_resource.h"
  36. #include "resource/sound_resource.h"
  37. #include "resource/sprite_resource.h"
  38. #include "resource/state_machine_resource.h"
  39. #include "resource/texture_resource.h"
  40. #include "resource/types.h"
  41. #include "resource/unit_resource.h"
  42. #include <algorithm>
  43. #include <inttypes.h>
  44. #if CROWN_PLATFORM_POSIX
  45. #include <signal.h>
  46. #endif
  47. LOG_SYSTEM(DATA_COMPILER, "data_compiler")
  48. #define CROWN_DATA_VERSIONS "data_versions.sjson"
  49. #define CROWN_DATA_INDEX "data_index.sjson"
  50. #define CROWN_DATA_MTIMES "data_mtimes.sjson"
  51. #define CROWN_DATA_DEPENDENCIES "data_dependencies.sjson"
  52. namespace crown
  53. {
  54. static volatile bool _quit = false;
  55. static void notify_add_file(const char* path)
  56. {
  57. TempAllocator512 ta;
  58. StringStream ss(ta);
  59. ss << "{\"type\":\"add_file\",\"path\":\"" << path << "\"}";
  60. console_server()->send(string_stream::c_str(ss));
  61. }
  62. static void notify_remove_file(const char* path)
  63. {
  64. TempAllocator512 ta;
  65. StringStream ss(ta);
  66. ss << "{\"type\":\"remove_file\",\"path\":\"" << path << "\"}";
  67. console_server()->send(string_stream::c_str(ss));
  68. }
  69. static void notify_add_tree(const char* path)
  70. {
  71. TempAllocator512 ta;
  72. StringStream ss(ta);
  73. ss << "{\"type\":\"add_tree\",\"path\":\"" << path << "\"}";
  74. console_server()->send(string_stream::c_str(ss));
  75. }
  76. static void notify_remove_tree(const char* path)
  77. {
  78. TempAllocator512 ta;
  79. StringStream ss(ta);
  80. ss << "{\"type\":\"remove_tree\",\"path\":\"" << path << "\"}";
  81. console_server()->send(string_stream::c_str(ss));
  82. }
  83. SourceIndex::SourceIndex()
  84. : _paths(default_allocator())
  85. {
  86. }
  87. void SourceIndex::scan_directory(FilesystemDisk& fs, const char* prefix, const char* directory)
  88. {
  89. Vector<DynamicString> files(default_allocator());
  90. fs.list_files(directory != NULL ? directory : "", files);
  91. for (u32 i = 0; i < vector::size(files); ++i)
  92. {
  93. TempAllocator512 ta;
  94. DynamicString file_i(ta);
  95. if (directory != NULL)
  96. {
  97. file_i += directory;
  98. file_i += '/';
  99. }
  100. file_i += files[i];
  101. if (fs.is_directory(file_i.c_str()))
  102. {
  103. scan_directory(fs, prefix, file_i.c_str());
  104. }
  105. else // Assume a regular file
  106. {
  107. DynamicString resource_name(ta);
  108. if (strcmp(prefix, "") != 0)
  109. {
  110. resource_name += prefix;
  111. resource_name += '/';
  112. }
  113. resource_name += file_i;
  114. Stat stat;
  115. stat = fs.stat(file_i.c_str());
  116. hash_map::set(_paths, resource_name, stat);
  117. notify_add_file(resource_name.c_str());
  118. }
  119. }
  120. }
  121. void SourceIndex::scan(const HashMap<DynamicString, DynamicString>& source_dirs)
  122. {
  123. auto cur = hash_map::begin(source_dirs);
  124. auto end = hash_map::end(source_dirs);
  125. for (; cur != end; ++cur)
  126. {
  127. HASH_MAP_SKIP_HOLE(source_dirs, cur);
  128. DynamicString prefix(default_allocator());
  129. path::join(prefix, cur->second.c_str(), cur->first.c_str());
  130. FilesystemDisk fs(default_allocator());
  131. fs.set_prefix(prefix.c_str());
  132. scan_directory(fs, cur->first.c_str(), NULL);
  133. }
  134. }
  135. struct LineReader
  136. {
  137. const char* _str;
  138. const u32 _len;
  139. u32 _pos;
  140. LineReader(const char* str)
  141. : _str(str)
  142. , _len(strlen32(str))
  143. , _pos(0)
  144. {
  145. }
  146. void read_line(DynamicString& line)
  147. {
  148. const char* s = &_str[_pos];
  149. const char* nl = strnl(s);
  150. _pos += u32(nl - s);
  151. line.set(s, u32(nl - s));
  152. }
  153. bool eof()
  154. {
  155. return _str[_pos] == '\0';
  156. }
  157. };
  158. static void console_command_compile(ConsoleServer& cs, TCPSocket client, const char* json, void* user_data)
  159. {
  160. TempAllocator4096 ta;
  161. JsonObject obj(ta);
  162. DynamicString id(ta);
  163. DynamicString data_dir(ta);
  164. DynamicString platform(ta);
  165. sjson::parse(obj, json);
  166. sjson::parse_string(id, obj["id"]);
  167. sjson::parse_string(data_dir, obj["data_dir"]);
  168. sjson::parse_string(platform, obj["platform"]);
  169. {
  170. TempAllocator512 ta;
  171. StringStream ss(ta);
  172. ss << "{\"type\":\"compile\",\"id\":\"" << id.c_str() << "\",\"start\":true}";
  173. cs.send(client, string_stream::c_str(ss));
  174. }
  175. bool succ = ((DataCompiler*)user_data)->compile(data_dir.c_str(), platform.c_str());
  176. {
  177. TempAllocator512 ta;
  178. StringStream ss(ta);
  179. ss << "{\"type\":\"compile\",\"id\":\"" << id.c_str() << "\",\"success\":" << (succ ? "true" : "false") << "}";
  180. cs.send(client, string_stream::c_str(ss));
  181. }
  182. }
  183. static void console_command_quit(ConsoleServer& /*cs*/, TCPSocket /*client*/, const char* /*json*/, void* /*user_data*/)
  184. {
  185. _quit = true;
  186. }
  187. static Buffer read(FilesystemDisk& data_fs, const char* filename)
  188. {
  189. Buffer buffer(default_allocator());
  190. // FIXME: better return NULL in Filesystem::open().
  191. if (data_fs.exists(filename))
  192. {
  193. File* file = data_fs.open(filename, FileOpenMode::READ);
  194. if (file)
  195. {
  196. u32 size = file->size();
  197. if (size == 0)
  198. {
  199. data_fs.close(*file);
  200. return buffer;
  201. }
  202. array::resize(buffer, size);
  203. file->read(array::begin(buffer), size);
  204. data_fs.close(*file);
  205. }
  206. }
  207. return buffer;
  208. }
  209. static void read_data_versions(HashMap<DynamicString, u32>& versions, FilesystemDisk& data_fs, const char* filename)
  210. {
  211. Buffer json = read(data_fs, filename);
  212. TempAllocator512 ta;
  213. JsonObject obj(ta);
  214. sjson::parse(obj, json);
  215. auto cur = json_object::begin(obj);
  216. auto end = json_object::end(obj);
  217. for (; cur != end; ++cur)
  218. {
  219. JSON_OBJECT_SKIP_HOLE(obj, cur);
  220. TempAllocator256 ta;
  221. DynamicString type(ta);
  222. type.set(cur->first.data(), cur->first.length());
  223. hash_map::set(versions, type, (u32)sjson::parse_int(cur->second));
  224. }
  225. }
  226. static void read_data_index(HashMap<StringId64, DynamicString>& index, FilesystemDisk& data_fs, const char* filename)
  227. {
  228. Buffer json = read(data_fs, filename);
  229. TempAllocator512 ta;
  230. JsonObject obj(ta);
  231. sjson::parse(obj, json);
  232. auto cur = json_object::begin(obj);
  233. auto end = json_object::end(obj);
  234. for (; cur != end; ++cur)
  235. {
  236. JSON_OBJECT_SKIP_HOLE(obj, cur);
  237. TempAllocator256 ta;
  238. StringId64 dst_name;
  239. DynamicString src_path(ta);
  240. dst_name.parse(cur->first.data());
  241. sjson::parse_string(src_path, cur->second);
  242. hash_map::set(index, dst_name, src_path);
  243. }
  244. }
  245. static void read_data_mtimes(HashMap<StringId64, u64>& mtimes, FilesystemDisk& data_fs, const char* filename)
  246. {
  247. Buffer json = read(data_fs, filename);
  248. TempAllocator128 ta;
  249. JsonObject obj(ta);
  250. sjson::parse(obj, json);
  251. auto cur = json_object::begin(obj);
  252. auto end = json_object::end(obj);
  253. for (; cur != end; ++cur)
  254. {
  255. JSON_OBJECT_SKIP_HOLE(obj, cur);
  256. TempAllocator64 ta;
  257. StringId64 dst_name;
  258. DynamicString mtime_json(ta);
  259. dst_name.parse(cur->first.data());
  260. sjson::parse_string(mtime_json, cur->second);
  261. u64 mtime;
  262. sscanf(mtime_json.c_str(), "%" SCNu64, &mtime);
  263. hash_map::set(mtimes, dst_name, mtime);
  264. }
  265. }
  266. static void read_data_dependencies(DataCompiler& dc, FilesystemDisk& data_fs, const char* filename)
  267. {
  268. Buffer json = read(data_fs, filename);
  269. TempAllocator1024 ta;
  270. JsonObject obj(ta);
  271. sjson::parse(obj, json);
  272. auto cur = json_object::begin(obj);
  273. auto end = json_object::end(obj);
  274. for (; cur != end; ++cur)
  275. {
  276. JSON_OBJECT_SKIP_HOLE(obj, cur);
  277. StringId64 dst_name;
  278. dst_name.parse(cur->first.data());
  279. JsonArray dependency_array(ta);
  280. sjson::parse_array(dependency_array, cur->second);
  281. for (u32 i = 0; i < array::size(dependency_array); ++i)
  282. {
  283. DynamicString src_path(ta);
  284. sjson::parse_string(src_path, dependency_array[i]);
  285. if (src_path.has_prefix("//r "))
  286. {
  287. dc.add_requirement(dst_name, src_path.c_str() + 4);
  288. }
  289. else if (src_path.has_prefix("//- "))
  290. {
  291. dc.add_dependency(dst_name, src_path.c_str() + 4);
  292. }
  293. else // Assume regular dependency
  294. {
  295. dc.add_dependency(dst_name, src_path.c_str());
  296. }
  297. }
  298. }
  299. }
  300. static void write_data_index(FilesystemDisk& data_fs, const char* filename, const HashMap<StringId64, DynamicString>& index)
  301. {
  302. StringStream ss(default_allocator());
  303. File* file = data_fs.open(filename, FileOpenMode::WRITE);
  304. if (file)
  305. {
  306. auto cur = hash_map::begin(index);
  307. auto end = hash_map::end(index);
  308. for (; cur != end; ++cur)
  309. {
  310. HASH_MAP_SKIP_HOLE(index, cur);
  311. TempAllocator256 ta;
  312. DynamicString str(ta);
  313. str.from_string_id(cur->first);
  314. ss << "\"" << str.c_str() << "\" = \"" << cur->second.c_str() << "\"\n";
  315. }
  316. file->write(string_stream::c_str(ss), strlen32(string_stream::c_str(ss)));
  317. data_fs.close(*file);
  318. }
  319. }
  320. static void write_data_versions(FilesystemDisk& data_fs, const char* filename, const HashMap<DynamicString, u32>& versions)
  321. {
  322. StringStream ss(default_allocator());
  323. File* file = data_fs.open(filename, FileOpenMode::WRITE);
  324. if (file)
  325. {
  326. auto cur = hash_map::begin(versions);
  327. auto end = hash_map::end(versions);
  328. for (; cur != end; ++cur)
  329. {
  330. HASH_MAP_SKIP_HOLE(versions, cur);
  331. ss << cur->first.c_str() << " = " << cur->second << "\n";
  332. }
  333. file->write(string_stream::c_str(ss), strlen32(string_stream::c_str(ss)));
  334. data_fs.close(*file);
  335. }
  336. }
  337. static void write_data_mtimes(FilesystemDisk& data_fs, const char* filename, const HashMap<StringId64, u64>& mtimes)
  338. {
  339. StringStream ss(default_allocator());
  340. File* file = data_fs.open(filename, FileOpenMode::WRITE);
  341. if (file)
  342. {
  343. auto cur = hash_map::begin(mtimes);
  344. auto end = hash_map::end(mtimes);
  345. for (; cur != end; ++cur)
  346. {
  347. HASH_MAP_SKIP_HOLE(mtimes, cur);
  348. TempAllocator64 ta;
  349. DynamicString key(ta);
  350. key.from_string_id(cur->first);
  351. ss << "\"" << key.c_str() << "\" = \"" << cur->second << "\"\n";
  352. }
  353. file->write(string_stream::c_str(ss), strlen32(string_stream::c_str(ss)));
  354. data_fs.close(*file);
  355. }
  356. }
  357. static void write_data_dependencies(FilesystemDisk& data_fs, const char* filename, const HashMap<StringId64, DynamicString>& index, const HashMap<StringId64, HashMap<DynamicString, u32> >& dependencies, const HashMap<StringId64, HashMap<DynamicString, u32> >& requirements)
  358. {
  359. StringStream ss(default_allocator());
  360. File* file = data_fs.open(filename, FileOpenMode::WRITE);
  361. if (file)
  362. {
  363. auto cur = hash_map::begin(index);
  364. auto end = hash_map::end(index);
  365. for (; cur != end; ++cur)
  366. {
  367. HASH_MAP_SKIP_HOLE(index, cur);
  368. HashMap<DynamicString, u32> deps_deffault(default_allocator());
  369. const HashMap<DynamicString, u32>& deps = hash_map::get(dependencies, cur->first, deps_deffault);
  370. HashMap<DynamicString, u32> reqs_deffault(default_allocator());
  371. const HashMap<DynamicString, u32>& reqs = hash_map::get(requirements, cur->first, reqs_deffault);
  372. // Skip if data has no dependencies
  373. if (&deps == &deps_deffault && &reqs == &reqs_deffault)
  374. continue;
  375. TempAllocator64 ta;
  376. DynamicString key(ta);
  377. key.from_string_id(cur->first);
  378. ss << "\"" << key.c_str() << "\" = [\n";
  379. // Write all dependencies
  380. auto deps_cur = hash_map::begin(deps);
  381. auto deps_end = hash_map::end(deps);
  382. for (; deps_cur != deps_end; ++deps_cur)
  383. {
  384. HASH_MAP_SKIP_HOLE(deps, deps_cur);
  385. ss << " \"//- " << deps_cur->first.c_str() << "\"\n";
  386. }
  387. // Write all requirements
  388. auto reqs_cur = hash_map::begin(reqs);
  389. auto reqs_end = hash_map::end(reqs);
  390. for (; reqs_cur != reqs_end; ++reqs_cur)
  391. {
  392. HASH_MAP_SKIP_HOLE(reqs, reqs_cur);
  393. ss << " \"//r " << reqs_cur->first.c_str() << "\"\n";
  394. }
  395. ss << "]\n";
  396. }
  397. file->write(string_stream::c_str(ss), strlen32(string_stream::c_str(ss)));
  398. data_fs.close(*file);
  399. }
  400. }
  401. DataCompiler::DataCompiler(const DeviceOptions& opts, ConsoleServer& cs)
  402. : _options(&opts)
  403. , _console_server(&cs)
  404. , _source_fs(default_allocator())
  405. , _source_dirs(default_allocator())
  406. , _compilers(default_allocator())
  407. , _globs(default_allocator())
  408. , _data_index(default_allocator())
  409. , _data_mtimes(default_allocator())
  410. , _data_dependencies(default_allocator())
  411. , _data_requirements(default_allocator())
  412. , _data_versions(default_allocator())
  413. , _file_monitor(default_allocator())
  414. {
  415. cs.register_command("compile", console_command_compile, this);
  416. cs.register_command("quit", console_command_quit, this);
  417. }
  418. DataCompiler::~DataCompiler()
  419. {
  420. if (_options->_server)
  421. _file_monitor.stop();
  422. }
  423. void DataCompiler::add_file(const char* path)
  424. {
  425. // Get source directory prefix
  426. TempAllocator512 ta;
  427. DynamicString source_dir(ta);
  428. source_dir = hash_map::get(_source_dirs, source_dir, source_dir);
  429. // Convert to DynamicString
  430. DynamicString str(ta);
  431. str.set(path, strlen32(path));
  432. // Get file status
  433. FilesystemDisk fs(default_allocator());
  434. fs.set_prefix(source_dir.c_str());
  435. Stat stat;
  436. stat = fs.stat(path);
  437. hash_map::set(_source_index._paths, str, stat);
  438. notify_add_file(path);
  439. }
  440. void DataCompiler::remove_file(const char* path)
  441. {
  442. // Convert to DynamicString
  443. TempAllocator512 ta;
  444. DynamicString str(ta);
  445. str.set(path, strlen32(path));
  446. hash_map::remove(_source_index._paths, str);
  447. notify_remove_file(path);
  448. }
  449. void DataCompiler::add_tree(const char* path)
  450. {
  451. #if CROWN_PLATFORM_LINUX
  452. // Get source directory prefix
  453. TempAllocator512 ta;
  454. DynamicString source_dir(ta);
  455. source_dir = hash_map::get(_source_dirs, source_dir, source_dir);
  456. // Scan the directory tree.
  457. // See file_monitor_linux.cpp:180.
  458. FilesystemDisk fs(default_allocator());
  459. fs.set_prefix(source_dir.c_str());
  460. _source_index.scan_directory(fs, "", path);
  461. #endif // CROWN_PLATFORM_LINUX
  462. notify_add_tree(path);
  463. }
  464. void DataCompiler::remove_tree(const char* path)
  465. {
  466. notify_remove_tree(path);
  467. }
  468. void DataCompiler::map_source_dir(const char* name, const char* source_dir)
  469. {
  470. TempAllocator256 ta;
  471. DynamicString sname(ta);
  472. DynamicString sdir(ta);
  473. sname.set(name, strlen32(name));
  474. sdir.set(source_dir, strlen32(source_dir));
  475. hash_map::set(_source_dirs, sname, sdir);
  476. }
  477. void DataCompiler::source_dir(const char* resource_name, DynamicString& source_dir)
  478. {
  479. const char* slash = strchr(resource_name, '/');
  480. TempAllocator256 ta;
  481. DynamicString source_name(ta);
  482. if (slash != NULL)
  483. source_name.set(resource_name, u32(slash - resource_name));
  484. else
  485. source_name.set("", 0);
  486. DynamicString deffault(ta);
  487. DynamicString empty(ta);
  488. empty = "";
  489. deffault = hash_map::get(_source_dirs, empty, empty);
  490. source_dir = hash_map::get(_source_dirs, source_name, deffault);
  491. }
  492. void DataCompiler::add_ignore_glob(const char* glob)
  493. {
  494. TempAllocator64 ta;
  495. DynamicString str(ta);
  496. str.set(glob, strlen32(glob));
  497. vector::push_back(_globs, str);
  498. }
  499. void DataCompiler::scan_and_restore(const char* data_dir)
  500. {
  501. // Scan all source directories
  502. s64 time_start = time::now();
  503. // FIXME: refactor this whole garbage
  504. Array<const char*> directories(default_allocator());
  505. auto cur = hash_map::begin(_source_dirs);
  506. auto end = hash_map::end(_source_dirs);
  507. for (; cur != end; ++cur)
  508. {
  509. HASH_MAP_SKIP_HOLE(_source_dirs, cur);
  510. DynamicString prefix(default_allocator());
  511. path::join(prefix, cur->second.c_str(), cur->first.c_str());
  512. char* str = (char*)default_allocator().allocate(prefix.length() + 1);
  513. strcpy(str, prefix.c_str());
  514. array::push_back(directories, (const char*)str);
  515. _source_fs.set_prefix(prefix.c_str());
  516. if (_source_fs.exists(CROWN_DATAIGNORE))
  517. {
  518. File& file = *_source_fs.open(CROWN_DATAIGNORE, FileOpenMode::READ);
  519. const u32 size = file.size();
  520. char* data = (char*)default_allocator().allocate(size + 1);
  521. file.read(data, size);
  522. data[size] = '\0';
  523. _source_fs.close(file);
  524. LineReader lr(data);
  525. while (!lr.eof())
  526. {
  527. TempAllocator512 ta;
  528. DynamicString line(ta);
  529. lr.read_line(line);
  530. line.trim();
  531. if (line.empty() || line.has_prefix("#"))
  532. continue;
  533. add_ignore_glob(line.c_str());
  534. }
  535. default_allocator().deallocate(data);
  536. }
  537. }
  538. _source_index.scan(_source_dirs);
  539. logi(DATA_COMPILER, "Scanned data in %.2fs", time::seconds(time::now() - time_start));
  540. // Restore state from previous run
  541. time_start = time::now();
  542. FilesystemDisk data_fs(default_allocator());
  543. data_fs.set_prefix(data_dir);
  544. read_data_versions(_data_versions, data_fs, CROWN_DATA_VERSIONS);
  545. read_data_index(_data_index, data_fs, CROWN_DATA_INDEX);
  546. read_data_mtimes(_data_mtimes, data_fs, CROWN_DATA_MTIMES);
  547. read_data_dependencies(*this, data_fs, CROWN_DATA_DEPENDENCIES);
  548. logi(DATA_COMPILER, "Restored state in %.2fs", time::seconds(time::now() - time_start));
  549. if (_options->_server)
  550. {
  551. // Start file monitor
  552. time_start = time::now();
  553. _file_monitor.start(array::size(directories)
  554. , array::begin(directories)
  555. , true
  556. , file_monitor_callback
  557. , this
  558. );
  559. logi(DATA_COMPILER, "Started file monitor in %.2fs", time::seconds(time::now() - time_start));
  560. }
  561. // Cleanup
  562. for (u32 i = 0, n = array::size(directories); i < n; ++i)
  563. default_allocator().deallocate((void*)directories[n-1-i]);
  564. }
  565. void DataCompiler::save(const char* data_dir)
  566. {
  567. s64 time_start = time::now();
  568. FilesystemDisk data_fs(default_allocator());
  569. data_fs.set_prefix(data_dir);
  570. write_data_index(data_fs, CROWN_DATA_INDEX, _data_index);
  571. write_data_versions(data_fs, CROWN_DATA_VERSIONS, _data_versions);
  572. write_data_mtimes(data_fs, CROWN_DATA_MTIMES, _data_mtimes);
  573. write_data_dependencies(data_fs, CROWN_DATA_DEPENDENCIES, _data_index, _data_dependencies, _data_requirements);
  574. logi(DATA_COMPILER, "Saved state in %.2fs", time::seconds(time::now() - time_start));
  575. }
  576. bool DataCompiler::dependency_changed(const DynamicString& src_path, ResourceId id, u64 dst_mtime)
  577. {
  578. TempAllocator1024 ta;
  579. DynamicString path(ta);
  580. destination_path(path, id);
  581. Stat src_stat;
  582. src_stat = hash_map::get(_source_index._paths, src_path, src_stat);
  583. if (src_stat.mtime > dst_mtime)
  584. return true;
  585. HashMap<DynamicString, u32> deffault(default_allocator());
  586. HashMap<DynamicString, u32>& deps = hash_map::get(_data_dependencies, id, deffault);
  587. auto cur = hash_map::begin(deps);
  588. auto end = hash_map::end(deps);
  589. for (; cur != end; ++cur)
  590. {
  591. HASH_MAP_SKIP_HOLE(deps, cur);
  592. if (src_path == cur->first)
  593. continue;
  594. if (dependency_changed(cur->first, resource_id(cur->first.c_str()), dst_mtime))
  595. return true;
  596. }
  597. return false;
  598. }
  599. bool DataCompiler::version_changed(const DynamicString& src_path, ResourceId id)
  600. {
  601. const char* type = path::extension(src_path.c_str());
  602. if (data_version_stored(type) != data_version(type))
  603. return true;
  604. HashMap<DynamicString, u32> deffault(default_allocator());
  605. HashMap<DynamicString, u32>& deps = hash_map::get(_data_dependencies, id, deffault);
  606. auto cur = hash_map::begin(deps);
  607. auto end = hash_map::end(deps);
  608. for (; cur != end; ++cur)
  609. {
  610. HASH_MAP_SKIP_HOLE(deps, cur);
  611. if (src_path == cur->first)
  612. continue;
  613. if (version_changed(cur->first, resource_id(cur->first.c_str())))
  614. return true;
  615. }
  616. return false;
  617. }
  618. bool DataCompiler::should_ignore(const char* path)
  619. {
  620. for (u32 ii = 0, nn = vector::size(_globs); ii < nn; ++ii)
  621. {
  622. if (wildcmp(_globs[ii].c_str(), path))
  623. return true;
  624. }
  625. return false;
  626. }
  627. bool DataCompiler::compile(const char* data_dir, const char* platform)
  628. {
  629. const s64 time_start = time::now();
  630. FilesystemDisk data_fs(default_allocator());
  631. data_fs.set_prefix(data_dir);
  632. data_fs.create_directory("");
  633. data_fs.create_directory(CROWN_DATA_DIRECTORY);
  634. data_fs.create_directory(CROWN_TEMP_DIRECTORY);
  635. // Find the set of resources to be compiled
  636. Vector<DynamicString> to_compile(default_allocator());
  637. auto cur = hash_map::begin(_source_index._paths);
  638. auto end = hash_map::end(_source_index._paths);
  639. for (; cur != end; ++cur)
  640. {
  641. HASH_MAP_SKIP_HOLE(_source_index._paths, cur);
  642. const DynamicString& src_path = cur->first;
  643. const char* filename = src_path.c_str();
  644. const char* type = path::extension(filename);
  645. if (should_ignore(filename))
  646. continue;
  647. if (type == NULL || !can_compile(type))
  648. {
  649. loge(DATA_COMPILER, "Unknown resource file: '%s'", filename);
  650. loge(DATA_COMPILER, "Append matching pattern to " CROWN_DATAIGNORE " to ignore it");
  651. continue;
  652. }
  653. ResourceId id = resource_id(filename);
  654. TempAllocator256 ta;
  655. DynamicString path(ta);
  656. destination_path(path, id);
  657. u64 dst_mtime = 0;
  658. dst_mtime = hash_map::get(_data_mtimes, id, dst_mtime);
  659. Stat src_stat;
  660. src_stat = hash_map::get(_source_index._paths, src_path, src_stat);
  661. bool source_never_compiled_before = hash_map::has(_data_index, id) == false;
  662. bool source_has_been_changed = src_stat.mtime > dst_mtime;
  663. bool source_dependency_changed = dependency_changed(src_path, id, dst_mtime);
  664. bool data_version_changed = data_version_stored(type) != data_version(type);
  665. bool data_version_dependency_changed = version_changed(src_path, id);
  666. if (source_never_compiled_before
  667. || source_has_been_changed
  668. || source_dependency_changed
  669. || data_version_changed
  670. || data_version_dependency_changed
  671. )
  672. {
  673. vector::push_back(to_compile, src_path);
  674. }
  675. }
  676. // Sort to_compile so that ".package" resources get compiled last
  677. std::sort(vector::begin(to_compile), vector::end(to_compile), [](const DynamicString& resource_a, const DynamicString& resource_b)
  678. {
  679. #define PACKAGE ".package"
  680. if ( resource_a.has_suffix(PACKAGE) && !resource_b.has_suffix(PACKAGE))
  681. return false;
  682. if (!resource_a.has_suffix(PACKAGE) && resource_b.has_suffix(PACKAGE))
  683. return true;
  684. return resource_a < resource_b;
  685. #undef PACKAGE
  686. });
  687. bool success = true;
  688. // Compile all changed resources
  689. for (u32 i = 0; i < vector::size(to_compile); ++i)
  690. {
  691. const DynamicString& src_path = to_compile[i];
  692. const char* filename = src_path.c_str();
  693. const char* type = path::extension(filename);
  694. if (type == NULL)
  695. continue;
  696. TempAllocator1024 ta;
  697. DynamicString path(ta);
  698. // Build destination file path
  699. ResourceId id = resource_id(filename);
  700. destination_path(path, id);
  701. logi(DATA_COMPILER, "%s", src_path.c_str());
  702. // Compile data
  703. ResourceTypeData rtd;
  704. rtd.version = 0;
  705. rtd.compiler = NULL;
  706. DynamicString type_str(ta);
  707. type_str = type;
  708. rtd = hash_map::get(_compilers, type_str, rtd);
  709. {
  710. // Reset dependencies and references since data could not depend
  711. // anymore on any of those.
  712. HashMap<DynamicString, u32> dependencies_deffault(default_allocator());
  713. hash_map::clear(hash_map::get(_data_dependencies, id, dependencies_deffault));
  714. HashMap<DynamicString, u32> requirements_deffault(default_allocator());
  715. hash_map::clear(hash_map::get(_data_requirements, id, requirements_deffault));
  716. // Compile data
  717. Buffer output(default_allocator());
  718. CompileOptions opts(*this, data_fs, id, src_path, output, platform);
  719. success = rtd.compiler(opts) == 0;
  720. if (success)
  721. {
  722. File* outf = data_fs.open(path.c_str(), FileOpenMode::WRITE);
  723. u32 size = array::size(output);
  724. u32 written = outf->write(array::begin(output), size);
  725. data_fs.close(*outf);
  726. success = size == written;
  727. }
  728. }
  729. if (success)
  730. {
  731. hash_map::set(_data_index, id, src_path);
  732. hash_map::set(_data_versions, type_str, rtd.version);
  733. hash_map::set(_data_mtimes, id, data_fs.last_modified_time(path.c_str()));
  734. }
  735. else
  736. {
  737. loge(DATA_COMPILER, "Failed to compile data");
  738. break;
  739. }
  740. }
  741. if (success)
  742. {
  743. if (vector::size(to_compile))
  744. logi(DATA_COMPILER, "Compiled data in %.2fs", time::seconds(time::now() - time_start));
  745. else
  746. logi(DATA_COMPILER, "Data is up to date");
  747. }
  748. return success;
  749. }
  750. void DataCompiler::register_compiler(const char* type, u32 version, CompileFunction compiler)
  751. {
  752. TempAllocator64 ta;
  753. DynamicString type_str(ta);
  754. type_str = type;
  755. CE_ASSERT(!hash_map::has(_compilers, type_str), "Type already registered");
  756. CE_ENSURE(NULL != compiler);
  757. ResourceTypeData rtd;
  758. rtd.version = version;
  759. rtd.compiler = compiler;
  760. hash_map::set(_compilers, type_str, rtd);
  761. }
  762. u32 DataCompiler::data_version(const char* type)
  763. {
  764. TempAllocator64 ta;
  765. DynamicString type_str(ta);
  766. type_str = type;
  767. ResourceTypeData rtd;
  768. rtd.version = COMPILER_NOT_FOUND;
  769. rtd.compiler = NULL;
  770. return hash_map::get(_compilers, type_str, rtd).version;
  771. }
  772. u32 DataCompiler::data_version_stored(const char* type)
  773. {
  774. TempAllocator256 ta;
  775. DynamicString ds(ta);
  776. ds = type;
  777. u32 version = UINT32_MAX;
  778. return hash_map::get(_data_versions, ds, version);
  779. }
  780. void DataCompiler::add_dependency_internal(HashMap<StringId64, HashMap<DynamicString, u32> >& dependencies, ResourceId id, const char* dependency)
  781. {
  782. HashMap<DynamicString, u32> deps_deffault(default_allocator());
  783. if (hash_map::has(dependencies, id))
  784. {
  785. HashMap<DynamicString, u32>& deps = hash_map::get(dependencies, id, deps_deffault);
  786. TempAllocator256 ta;
  787. DynamicString dependency_ds(ta);
  788. dependency_ds = dependency;
  789. hash_map::set(deps, dependency_ds, 0u);
  790. }
  791. else
  792. {
  793. TempAllocator256 ta;
  794. DynamicString dependency_ds(ta);
  795. dependency_ds = dependency;
  796. hash_map::set(deps_deffault, dependency_ds, 0u);
  797. hash_map::set(dependencies, id, deps_deffault);
  798. }
  799. }
  800. void DataCompiler::add_dependency(ResourceId id, const char* dependency)
  801. {
  802. add_dependency_internal(_data_dependencies, id, dependency);
  803. }
  804. void DataCompiler::add_requirement(ResourceId id, const char* requirement)
  805. {
  806. add_dependency_internal(_data_requirements, id, requirement);
  807. }
  808. bool DataCompiler::can_compile(const char* type)
  809. {
  810. TempAllocator64 ta;
  811. DynamicString type_str(ta);
  812. type_str = type;
  813. return hash_map::has(_compilers, type_str);
  814. }
  815. void DataCompiler::error(const char* msg, va_list args)
  816. {
  817. vloge(DATA_COMPILER, msg, args);
  818. }
  819. void DataCompiler::file_monitor_callback(FileMonitorEvent::Enum fme, bool is_dir, const char* path, const char* path_renamed)
  820. {
  821. TempAllocator512 ta;
  822. DynamicString source_dir(ta);
  823. DynamicString resource_name(ta);
  824. // Find source directory by matching mapped
  825. // directory prefix with `path`.
  826. auto cur = hash_map::begin(_source_dirs);
  827. auto end = hash_map::end(_source_dirs);
  828. for (; cur != end; ++cur)
  829. {
  830. HASH_MAP_SKIP_HOLE(_source_dirs, cur);
  831. path::join(source_dir, cur->second.c_str(), cur->first.c_str());
  832. if (str_has_prefix(path, source_dir.c_str()))
  833. break;
  834. }
  835. if (cur != end)
  836. {
  837. // All events received must refer to directories
  838. // mapped with map_source_dir().
  839. const char* filename = &path[source_dir.length()+1];
  840. path::join(resource_name, cur->first.c_str(), filename);
  841. #if 0
  842. logi(DATA_COMPILER, "path : %s", path);
  843. logi(DATA_COMPILER, "source_dir : %s", source_dir.c_str());
  844. logi(DATA_COMPILER, "resource_name: %s", resource_name.c_str());
  845. #endif
  846. switch (fme)
  847. {
  848. case FileMonitorEvent::CREATED:
  849. if (!is_dir)
  850. add_file(resource_name.c_str());
  851. else
  852. add_tree(resource_name.c_str());
  853. break;
  854. case FileMonitorEvent::DELETED:
  855. if (!is_dir)
  856. remove_file(resource_name.c_str());
  857. else
  858. remove_tree(resource_name.c_str());
  859. break;
  860. case FileMonitorEvent::RENAMED:
  861. {
  862. DynamicString resource_name_renamed(ta);
  863. path::join(resource_name_renamed, cur->first.c_str(), &path_renamed[source_dir.length()+1]);
  864. if (!is_dir)
  865. {
  866. remove_file(resource_name.c_str());
  867. add_file(resource_name_renamed.c_str());
  868. }
  869. else
  870. {
  871. remove_tree(resource_name.c_str());
  872. add_tree(resource_name_renamed.c_str());
  873. }
  874. }
  875. break;
  876. case FileMonitorEvent::CHANGED:
  877. if (!is_dir)
  878. {
  879. FilesystemDisk fs(default_allocator());
  880. fs.set_prefix(source_dir.c_str());
  881. Stat stat;
  882. stat = fs.stat(filename);
  883. hash_map::set(_source_index._paths, resource_name, stat);
  884. }
  885. break;
  886. default:
  887. CE_ASSERT(false, "Unknown FileMonitorEvent: %d", fme);
  888. break;
  889. }
  890. }
  891. }
  892. void DataCompiler::file_monitor_callback(void* thiz, FileMonitorEvent::Enum fme, bool is_dir, const char* path_original, const char* path_modified)
  893. {
  894. ((DataCompiler*)thiz)->file_monitor_callback(fme, is_dir, path_original, path_modified);
  895. }
  896. int main_data_compiler(const DeviceOptions& opts)
  897. {
  898. #if CROWN_PLATFORM_POSIX
  899. struct sigaction old_SIGINT;
  900. struct sigaction act;
  901. act.sa_handler = [](int /*signum*/) { _quit = true; };
  902. sigemptyset(&act.sa_mask);
  903. act.sa_flags = 0;
  904. sigaction(SIGINT, NULL, &old_SIGINT);
  905. sigaction(SIGINT, &act, NULL);
  906. #endif // CROWN_PLATFORM_POSIX
  907. console_server_globals::init();
  908. console_server()->listen(CROWN_DEFAULT_COMPILER_PORT, opts._wait_console);
  909. namespace cor = config_resource_internal;
  910. namespace ftr = font_resource_internal;
  911. namespace lur = lua_resource_internal;
  912. namespace lvr = level_resource_internal;
  913. namespace mhr = mesh_resource_internal;
  914. namespace mtr = material_resource_internal;
  915. namespace pcr = physics_config_resource_internal;
  916. namespace phr = physics_resource_internal;
  917. namespace pkr = package_resource_internal;
  918. namespace sar = sprite_animation_resource_internal;
  919. namespace sdr = sound_resource_internal;
  920. namespace shr = shader_resource_internal;
  921. namespace smr = state_machine_internal;
  922. namespace spr = sprite_resource_internal;
  923. namespace txr = texture_resource_internal;
  924. namespace utr = unit_resource_internal;
  925. DataCompiler* dc = CE_NEW(default_allocator(), DataCompiler)(opts, *console_server());
  926. dc->register_compiler("config", RESOURCE_VERSION_CONFIG, cor::compile);
  927. dc->register_compiler("font", RESOURCE_VERSION_FONT, ftr::compile);
  928. dc->register_compiler("level", RESOURCE_VERSION_LEVEL, lvr::compile);
  929. dc->register_compiler("material", RESOURCE_VERSION_MATERIAL, mtr::compile);
  930. dc->register_compiler("mesh", RESOURCE_VERSION_MESH, mhr::compile);
  931. dc->register_compiler("package", RESOURCE_VERSION_PACKAGE, pkr::compile);
  932. dc->register_compiler("physics_config", RESOURCE_VERSION_PHYSICS_CONFIG, pcr::compile);
  933. dc->register_compiler("lua", RESOURCE_VERSION_SCRIPT, lur::compile);
  934. dc->register_compiler("shader", RESOURCE_VERSION_SHADER, shr::compile);
  935. dc->register_compiler("sound", RESOURCE_VERSION_SOUND, sdr::compile);
  936. dc->register_compiler("sprite", RESOURCE_VERSION_SPRITE, spr::compile);
  937. dc->register_compiler("sprite_animation", RESOURCE_VERSION_SPRITE_ANIMATION, sar::compile);
  938. dc->register_compiler("state_machine", RESOURCE_VERSION_STATE_MACHINE, smr::compile);
  939. dc->register_compiler("texture", RESOURCE_VERSION_TEXTURE, txr::compile);
  940. dc->register_compiler("unit", RESOURCE_VERSION_UNIT, utr::compile);
  941. // Add ignore globs
  942. dc->add_ignore_glob("*.bak");
  943. dc->add_ignore_glob("*.dds");
  944. dc->add_ignore_glob("*.importer_settings");
  945. dc->add_ignore_glob("*.ktx");
  946. dc->add_ignore_glob("*.ogg");
  947. dc->add_ignore_glob("*.png");
  948. dc->add_ignore_glob("*.pvr");
  949. dc->add_ignore_glob("*.swn"); // VIM swap file.
  950. dc->add_ignore_glob("*.swo"); // VIM swap file.
  951. dc->add_ignore_glob("*.swp"); // VIM swap file.
  952. dc->add_ignore_glob("*.tga");
  953. dc->add_ignore_glob("*.tmp");
  954. dc->add_ignore_glob("*.wav");
  955. dc->add_ignore_glob("*~");
  956. dc->add_ignore_glob(".*");
  957. dc->map_source_dir("", opts._source_dir.c_str());
  958. if (opts._map_source_dir_name)
  959. {
  960. dc->map_source_dir(opts._map_source_dir_name
  961. , opts._map_source_dir_prefix.c_str()
  962. );
  963. }
  964. dc->scan_and_restore(opts._data_dir.c_str());
  965. bool success = true;
  966. if (opts._server)
  967. {
  968. while (!_quit)
  969. {
  970. console_server()->update();
  971. os::sleep(60);
  972. }
  973. }
  974. else
  975. {
  976. success = dc->compile(opts._data_dir.c_str(), opts._platform);
  977. }
  978. dc->save(opts._data_dir.c_str());
  979. CE_DELETE(default_allocator(), dc);
  980. console_server_globals::shutdown();
  981. #if CROWN_PLATFORM_POSIX
  982. // Restore original handler
  983. sigaction(SIGINT, &old_SIGINT, NULL);
  984. #endif // CROWN_PLATFORM_POSIX
  985. return success ? EXIT_SUCCESS : EXIT_FAILURE;
  986. }
  987. } // namespace crown
  988. #endif // CROWN_CAN_COMPILE