| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194 |
- /*
- * Copyright (c) 2012-2020 Daniele Bartolini and individual contributors.
- * License: https://github.com/dbartolini/crown/blob/master/LICENSE
- */
- #include "config.h"
- #if CROWN_CAN_COMPILE
- #include "core/containers/hash_map.h"
- #include "core/containers/hash_set.h"
- #include "core/containers/vector.h"
- #include "core/filesystem/file.h"
- #include "core/filesystem/filesystem_disk.h"
- #include "core/filesystem/path.h"
- #include "core/json/json_object.h"
- #include "core/json/sjson.h"
- #include "core/memory/allocator.h"
- #include "core/memory/temp_allocator.h"
- #include "core/os.h"
- #include "core/strings/dynamic_string.h"
- #include "core/strings/string_stream.h"
- #include "core/time.h"
- #include "device/console_server.h"
- #include "device/device_options.h"
- #include "device/log.h"
- #include "resource/compile_options.h"
- #include "resource/config_resource.h"
- #include "resource/data_compiler.h"
- #include "resource/font_resource.h"
- #include "resource/level_resource.h"
- #include "resource/lua_resource.h"
- #include "resource/material_resource.h"
- #include "resource/mesh_resource.h"
- #include "resource/package_resource.h"
- #include "resource/physics_resource.h"
- #include "resource/resource_id.h"
- #include "resource/shader_resource.h"
- #include "resource/sound_resource.h"
- #include "resource/sprite_resource.h"
- #include "resource/state_machine_resource.h"
- #include "resource/texture_resource.h"
- #include "resource/types.h"
- #include "resource/unit_resource.h"
- #include <algorithm>
- #include <inttypes.h>
- #if CROWN_PLATFORM_POSIX
- #include <signal.h>
- #endif
- LOG_SYSTEM(DATA_COMPILER, "data_compiler")
- #define CROWN_DATA_VERSIONS "data_versions.sjson"
- #define CROWN_DATA_INDEX "data_index.sjson"
- #define CROWN_DATA_MTIMES "data_mtimes.sjson"
- #define CROWN_DATA_DEPENDENCIES "data_dependencies.sjson"
- namespace crown
- {
- static volatile bool _quit = false;
- static void notify_add_file(const char* path)
- {
- TempAllocator512 ta;
- StringStream ss(ta);
- ss << "{\"type\":\"add_file\",\"path\":\"" << path << "\"}";
- console_server()->send(string_stream::c_str(ss));
- }
- static void notify_remove_file(const char* path)
- {
- TempAllocator512 ta;
- StringStream ss(ta);
- ss << "{\"type\":\"remove_file\",\"path\":\"" << path << "\"}";
- console_server()->send(string_stream::c_str(ss));
- }
- static void notify_add_tree(const char* path)
- {
- TempAllocator512 ta;
- StringStream ss(ta);
- ss << "{\"type\":\"add_tree\",\"path\":\"" << path << "\"}";
- console_server()->send(string_stream::c_str(ss));
- }
- static void notify_remove_tree(const char* path)
- {
- TempAllocator512 ta;
- StringStream ss(ta);
- ss << "{\"type\":\"remove_tree\",\"path\":\"" << path << "\"}";
- console_server()->send(string_stream::c_str(ss));
- }
- SourceIndex::SourceIndex()
- : _paths(default_allocator())
- {
- }
- void SourceIndex::scan_directory(FilesystemDisk& fs, const char* prefix, const char* directory)
- {
- Vector<DynamicString> files(default_allocator());
- fs.list_files(directory != NULL ? directory : "", files);
- for (u32 i = 0; i < vector::size(files); ++i)
- {
- TempAllocator512 ta;
- DynamicString file_i(ta);
- if (directory != NULL)
- {
- file_i += directory;
- file_i += '/';
- }
- file_i += files[i];
- if (fs.is_directory(file_i.c_str()))
- {
- scan_directory(fs, prefix, file_i.c_str());
- }
- else // Assume a regular file
- {
- DynamicString resource_name(ta);
- if (strcmp(prefix, "") != 0)
- {
- resource_name += prefix;
- resource_name += '/';
- }
- resource_name += file_i;
- Stat stat;
- stat = fs.stat(file_i.c_str());
- hash_map::set(_paths, resource_name, stat);
- notify_add_file(resource_name.c_str());
- }
- }
- }
- void SourceIndex::scan(const HashMap<DynamicString, DynamicString>& source_dirs)
- {
- auto cur = hash_map::begin(source_dirs);
- auto end = hash_map::end(source_dirs);
- for (; cur != end; ++cur)
- {
- HASH_MAP_SKIP_HOLE(source_dirs, cur);
- DynamicString prefix(default_allocator());
- path::join(prefix, cur->second.c_str(), cur->first.c_str());
- FilesystemDisk fs(default_allocator());
- fs.set_prefix(prefix.c_str());
- scan_directory(fs, cur->first.c_str(), NULL);
- }
- }
- struct LineReader
- {
- const char* _str;
- const u32 _len;
- u32 _pos;
- LineReader(const char* str)
- : _str(str)
- , _len(strlen32(str))
- , _pos(0)
- {
- }
- void read_line(DynamicString& line)
- {
- const char* s = &_str[_pos];
- const char* nl = strnl(s);
- _pos += u32(nl - s);
- line.set(s, u32(nl - s));
- }
- bool eof()
- {
- return _str[_pos] == '\0';
- }
- };
- static void console_command_compile(ConsoleServer& cs, TCPSocket client, const char* json, void* user_data)
- {
- TempAllocator4096 ta;
- JsonObject obj(ta);
- DynamicString id(ta);
- DynamicString data_dir(ta);
- DynamicString platform(ta);
- sjson::parse(obj, json);
- sjson::parse_string(id, obj["id"]);
- sjson::parse_string(data_dir, obj["data_dir"]);
- sjson::parse_string(platform, obj["platform"]);
- {
- TempAllocator512 ta;
- StringStream ss(ta);
- ss << "{\"type\":\"compile\",\"id\":\"" << id.c_str() << "\",\"start\":true}";
- cs.send(client, string_stream::c_str(ss));
- }
- bool succ = ((DataCompiler*)user_data)->compile(data_dir.c_str(), platform.c_str());
- {
- TempAllocator512 ta;
- StringStream ss(ta);
- ss << "{\"type\":\"compile\",\"id\":\"" << id.c_str() << "\",\"success\":" << (succ ? "true" : "false") << "}";
- cs.send(client, string_stream::c_str(ss));
- }
- }
- static void console_command_quit(ConsoleServer& /*cs*/, TCPSocket /*client*/, const char* /*json*/, void* /*user_data*/)
- {
- _quit = true;
- }
- static Buffer read(FilesystemDisk& data_fs, const char* filename)
- {
- Buffer buffer(default_allocator());
- // FIXME: better return NULL in Filesystem::open().
- if (data_fs.exists(filename))
- {
- File* file = data_fs.open(filename, FileOpenMode::READ);
- if (file)
- {
- u32 size = file->size();
- if (size == 0)
- {
- data_fs.close(*file);
- return buffer;
- }
- array::resize(buffer, size);
- file->read(array::begin(buffer), size);
- data_fs.close(*file);
- }
- }
- return buffer;
- }
- static void read_data_versions(HashMap<DynamicString, u32>& versions, FilesystemDisk& data_fs, const char* filename)
- {
- Buffer json = read(data_fs, filename);
- TempAllocator512 ta;
- JsonObject obj(ta);
- sjson::parse(obj, json);
- auto cur = json_object::begin(obj);
- auto end = json_object::end(obj);
- for (; cur != end; ++cur)
- {
- JSON_OBJECT_SKIP_HOLE(obj, cur);
- TempAllocator256 ta;
- DynamicString type(ta);
- type.set(cur->first.data(), cur->first.length());
- hash_map::set(versions, type, (u32)sjson::parse_int(cur->second));
- }
- }
- static void read_data_index(HashMap<StringId64, DynamicString>& index, FilesystemDisk& data_fs, const char* filename)
- {
- Buffer json = read(data_fs, filename);
- TempAllocator512 ta;
- JsonObject obj(ta);
- sjson::parse(obj, json);
- auto cur = json_object::begin(obj);
- auto end = json_object::end(obj);
- for (; cur != end; ++cur)
- {
- JSON_OBJECT_SKIP_HOLE(obj, cur);
- TempAllocator256 ta;
- StringId64 dst_name;
- DynamicString src_path(ta);
- dst_name.parse(cur->first.data());
- sjson::parse_string(src_path, cur->second);
- hash_map::set(index, dst_name, src_path);
- }
- }
- static void read_data_mtimes(HashMap<StringId64, u64>& mtimes, FilesystemDisk& data_fs, const char* filename)
- {
- Buffer json = read(data_fs, filename);
- TempAllocator128 ta;
- JsonObject obj(ta);
- sjson::parse(obj, json);
- auto cur = json_object::begin(obj);
- auto end = json_object::end(obj);
- for (; cur != end; ++cur)
- {
- JSON_OBJECT_SKIP_HOLE(obj, cur);
- TempAllocator64 ta;
- StringId64 dst_name;
- DynamicString mtime_json(ta);
- dst_name.parse(cur->first.data());
- sjson::parse_string(mtime_json, cur->second);
- u64 mtime;
- sscanf(mtime_json.c_str(), "%" SCNu64, &mtime);
- hash_map::set(mtimes, dst_name, mtime);
- }
- }
- static void read_data_dependencies(DataCompiler& dc, FilesystemDisk& data_fs, const char* filename)
- {
- Buffer json = read(data_fs, filename);
- TempAllocator1024 ta;
- JsonObject obj(ta);
- sjson::parse(obj, json);
- auto cur = json_object::begin(obj);
- auto end = json_object::end(obj);
- for (; cur != end; ++cur)
- {
- JSON_OBJECT_SKIP_HOLE(obj, cur);
- StringId64 dst_name;
- dst_name.parse(cur->first.data());
- JsonArray dependency_array(ta);
- sjson::parse_array(dependency_array, cur->second);
- for (u32 i = 0; i < array::size(dependency_array); ++i)
- {
- DynamicString src_path(ta);
- sjson::parse_string(src_path, dependency_array[i]);
- if (src_path.has_prefix("//r "))
- {
- dc.add_requirement(dst_name, src_path.c_str() + 4);
- }
- else if (src_path.has_prefix("//- "))
- {
- dc.add_dependency(dst_name, src_path.c_str() + 4);
- }
- else // Assume regular dependency
- {
- dc.add_dependency(dst_name, src_path.c_str());
- }
- }
- }
- }
- static void write_data_index(FilesystemDisk& data_fs, const char* filename, const HashMap<StringId64, DynamicString>& index)
- {
- StringStream ss(default_allocator());
- File* file = data_fs.open(filename, FileOpenMode::WRITE);
- if (file)
- {
- auto cur = hash_map::begin(index);
- auto end = hash_map::end(index);
- for (; cur != end; ++cur)
- {
- HASH_MAP_SKIP_HOLE(index, cur);
- TempAllocator256 ta;
- DynamicString str(ta);
- str.from_string_id(cur->first);
- ss << "\"" << str.c_str() << "\" = \"" << cur->second.c_str() << "\"\n";
- }
- file->write(string_stream::c_str(ss), strlen32(string_stream::c_str(ss)));
- data_fs.close(*file);
- }
- }
- static void write_data_versions(FilesystemDisk& data_fs, const char* filename, const HashMap<DynamicString, u32>& versions)
- {
- StringStream ss(default_allocator());
- File* file = data_fs.open(filename, FileOpenMode::WRITE);
- if (file)
- {
- auto cur = hash_map::begin(versions);
- auto end = hash_map::end(versions);
- for (; cur != end; ++cur)
- {
- HASH_MAP_SKIP_HOLE(versions, cur);
- ss << cur->first.c_str() << " = " << cur->second << "\n";
- }
- file->write(string_stream::c_str(ss), strlen32(string_stream::c_str(ss)));
- data_fs.close(*file);
- }
- }
- static void write_data_mtimes(FilesystemDisk& data_fs, const char* filename, const HashMap<StringId64, u64>& mtimes)
- {
- StringStream ss(default_allocator());
- File* file = data_fs.open(filename, FileOpenMode::WRITE);
- if (file)
- {
- auto cur = hash_map::begin(mtimes);
- auto end = hash_map::end(mtimes);
- for (; cur != end; ++cur)
- {
- HASH_MAP_SKIP_HOLE(mtimes, cur);
- TempAllocator64 ta;
- DynamicString key(ta);
- key.from_string_id(cur->first);
- ss << "\"" << key.c_str() << "\" = \"" << cur->second << "\"\n";
- }
- file->write(string_stream::c_str(ss), strlen32(string_stream::c_str(ss)));
- data_fs.close(*file);
- }
- }
- 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)
- {
- StringStream ss(default_allocator());
- File* file = data_fs.open(filename, FileOpenMode::WRITE);
- if (file)
- {
- auto cur = hash_map::begin(index);
- auto end = hash_map::end(index);
- for (; cur != end; ++cur)
- {
- HASH_MAP_SKIP_HOLE(index, cur);
- HashMap<DynamicString, u32> deps_deffault(default_allocator());
- const HashMap<DynamicString, u32>& deps = hash_map::get(dependencies, cur->first, deps_deffault);
- HashMap<DynamicString, u32> reqs_deffault(default_allocator());
- const HashMap<DynamicString, u32>& reqs = hash_map::get(requirements, cur->first, reqs_deffault);
- // Skip if data has no dependencies
- if (&deps == &deps_deffault && &reqs == &reqs_deffault)
- continue;
- TempAllocator64 ta;
- DynamicString key(ta);
- key.from_string_id(cur->first);
- ss << "\"" << key.c_str() << "\" = [\n";
- // Write all dependencies
- auto deps_cur = hash_map::begin(deps);
- auto deps_end = hash_map::end(deps);
- for (; deps_cur != deps_end; ++deps_cur)
- {
- HASH_MAP_SKIP_HOLE(deps, deps_cur);
- ss << " \"//- " << deps_cur->first.c_str() << "\"\n";
- }
- // Write all requirements
- auto reqs_cur = hash_map::begin(reqs);
- auto reqs_end = hash_map::end(reqs);
- for (; reqs_cur != reqs_end; ++reqs_cur)
- {
- HASH_MAP_SKIP_HOLE(reqs, reqs_cur);
- ss << " \"//r " << reqs_cur->first.c_str() << "\"\n";
- }
- ss << "]\n";
- }
- file->write(string_stream::c_str(ss), strlen32(string_stream::c_str(ss)));
- data_fs.close(*file);
- }
- }
- DataCompiler::DataCompiler(const DeviceOptions& opts, ConsoleServer& cs)
- : _options(&opts)
- , _console_server(&cs)
- , _source_fs(default_allocator())
- , _source_dirs(default_allocator())
- , _compilers(default_allocator())
- , _globs(default_allocator())
- , _data_index(default_allocator())
- , _data_mtimes(default_allocator())
- , _data_dependencies(default_allocator())
- , _data_requirements(default_allocator())
- , _data_versions(default_allocator())
- , _file_monitor(default_allocator())
- {
- cs.register_command("compile", console_command_compile, this);
- cs.register_command("quit", console_command_quit, this);
- }
- DataCompiler::~DataCompiler()
- {
- if (_options->_server)
- _file_monitor.stop();
- }
- void DataCompiler::add_file(const char* path)
- {
- // Get source directory prefix
- TempAllocator512 ta;
- DynamicString source_dir(ta);
- source_dir = hash_map::get(_source_dirs, source_dir, source_dir);
- // Convert to DynamicString
- DynamicString str(ta);
- str.set(path, strlen32(path));
- // Get file status
- FilesystemDisk fs(default_allocator());
- fs.set_prefix(source_dir.c_str());
- Stat stat;
- stat = fs.stat(path);
- hash_map::set(_source_index._paths, str, stat);
- notify_add_file(path);
- }
- void DataCompiler::remove_file(const char* path)
- {
- // Convert to DynamicString
- TempAllocator512 ta;
- DynamicString str(ta);
- str.set(path, strlen32(path));
- hash_map::remove(_source_index._paths, str);
- notify_remove_file(path);
- }
- void DataCompiler::add_tree(const char* path)
- {
- #if CROWN_PLATFORM_LINUX
- // Get source directory prefix
- TempAllocator512 ta;
- DynamicString source_dir(ta);
- source_dir = hash_map::get(_source_dirs, source_dir, source_dir);
- // Scan the directory tree.
- // See file_monitor_linux.cpp:180.
- FilesystemDisk fs(default_allocator());
- fs.set_prefix(source_dir.c_str());
- _source_index.scan_directory(fs, "", path);
- #endif // CROWN_PLATFORM_LINUX
- notify_add_tree(path);
- }
- void DataCompiler::remove_tree(const char* path)
- {
- notify_remove_tree(path);
- }
- void DataCompiler::map_source_dir(const char* name, const char* source_dir)
- {
- TempAllocator256 ta;
- DynamicString sname(ta);
- DynamicString sdir(ta);
- sname.set(name, strlen32(name));
- sdir.set(source_dir, strlen32(source_dir));
- hash_map::set(_source_dirs, sname, sdir);
- }
- void DataCompiler::source_dir(const char* resource_name, DynamicString& source_dir)
- {
- const char* slash = strchr(resource_name, '/');
- TempAllocator256 ta;
- DynamicString source_name(ta);
- if (slash != NULL)
- source_name.set(resource_name, u32(slash - resource_name));
- else
- source_name.set("", 0);
- DynamicString deffault(ta);
- DynamicString empty(ta);
- empty = "";
- deffault = hash_map::get(_source_dirs, empty, empty);
- source_dir = hash_map::get(_source_dirs, source_name, deffault);
- }
- void DataCompiler::add_ignore_glob(const char* glob)
- {
- TempAllocator64 ta;
- DynamicString str(ta);
- str.set(glob, strlen32(glob));
- vector::push_back(_globs, str);
- }
- void DataCompiler::scan_and_restore(const char* data_dir)
- {
- // Scan all source directories
- s64 time_start = time::now();
- // FIXME: refactor this whole garbage
- Array<const char*> directories(default_allocator());
- auto cur = hash_map::begin(_source_dirs);
- auto end = hash_map::end(_source_dirs);
- for (; cur != end; ++cur)
- {
- HASH_MAP_SKIP_HOLE(_source_dirs, cur);
- DynamicString prefix(default_allocator());
- path::join(prefix, cur->second.c_str(), cur->first.c_str());
- char* str = (char*)default_allocator().allocate(prefix.length() + 1);
- strcpy(str, prefix.c_str());
- array::push_back(directories, (const char*)str);
- _source_fs.set_prefix(prefix.c_str());
- if (_source_fs.exists(CROWN_DATAIGNORE))
- {
- File& file = *_source_fs.open(CROWN_DATAIGNORE, FileOpenMode::READ);
- const u32 size = file.size();
- char* data = (char*)default_allocator().allocate(size + 1);
- file.read(data, size);
- data[size] = '\0';
- _source_fs.close(file);
- LineReader lr(data);
- while (!lr.eof())
- {
- TempAllocator512 ta;
- DynamicString line(ta);
- lr.read_line(line);
- line.trim();
- if (line.empty() || line.has_prefix("#"))
- continue;
- add_ignore_glob(line.c_str());
- }
- default_allocator().deallocate(data);
- }
- }
- _source_index.scan(_source_dirs);
- logi(DATA_COMPILER, "Scanned data in %.2fs", time::seconds(time::now() - time_start));
- // Restore state from previous run
- time_start = time::now();
- FilesystemDisk data_fs(default_allocator());
- data_fs.set_prefix(data_dir);
- read_data_versions(_data_versions, data_fs, CROWN_DATA_VERSIONS);
- read_data_index(_data_index, data_fs, CROWN_DATA_INDEX);
- read_data_mtimes(_data_mtimes, data_fs, CROWN_DATA_MTIMES);
- read_data_dependencies(*this, data_fs, CROWN_DATA_DEPENDENCIES);
- logi(DATA_COMPILER, "Restored state in %.2fs", time::seconds(time::now() - time_start));
- if (_options->_server)
- {
- // Start file monitor
- time_start = time::now();
- _file_monitor.start(array::size(directories)
- , array::begin(directories)
- , true
- , file_monitor_callback
- , this
- );
- logi(DATA_COMPILER, "Started file monitor in %.2fs", time::seconds(time::now() - time_start));
- }
- // Cleanup
- for (u32 i = 0, n = array::size(directories); i < n; ++i)
- default_allocator().deallocate((void*)directories[n-1-i]);
- }
- void DataCompiler::save(const char* data_dir)
- {
- s64 time_start = time::now();
- FilesystemDisk data_fs(default_allocator());
- data_fs.set_prefix(data_dir);
- write_data_index(data_fs, CROWN_DATA_INDEX, _data_index);
- write_data_versions(data_fs, CROWN_DATA_VERSIONS, _data_versions);
- write_data_mtimes(data_fs, CROWN_DATA_MTIMES, _data_mtimes);
- write_data_dependencies(data_fs, CROWN_DATA_DEPENDENCIES, _data_index, _data_dependencies, _data_requirements);
- logi(DATA_COMPILER, "Saved state in %.2fs", time::seconds(time::now() - time_start));
- }
- bool DataCompiler::dependency_changed(const DynamicString& src_path, ResourceId id, u64 dst_mtime)
- {
- TempAllocator1024 ta;
- DynamicString path(ta);
- destination_path(path, id);
- Stat src_stat;
- src_stat = hash_map::get(_source_index._paths, src_path, src_stat);
- if (src_stat.mtime > dst_mtime)
- return true;
- HashMap<DynamicString, u32> deffault(default_allocator());
- HashMap<DynamicString, u32>& deps = hash_map::get(_data_dependencies, id, deffault);
- auto cur = hash_map::begin(deps);
- auto end = hash_map::end(deps);
- for (; cur != end; ++cur)
- {
- HASH_MAP_SKIP_HOLE(deps, cur);
- if (src_path == cur->first)
- continue;
- if (dependency_changed(cur->first, resource_id(cur->first.c_str()), dst_mtime))
- return true;
- }
- return false;
- }
- bool DataCompiler::version_changed(const DynamicString& src_path, ResourceId id)
- {
- const char* type = path::extension(src_path.c_str());
- if (data_version_stored(type) != data_version(type))
- return true;
- HashMap<DynamicString, u32> deffault(default_allocator());
- HashMap<DynamicString, u32>& deps = hash_map::get(_data_dependencies, id, deffault);
- auto cur = hash_map::begin(deps);
- auto end = hash_map::end(deps);
- for (; cur != end; ++cur)
- {
- HASH_MAP_SKIP_HOLE(deps, cur);
- if (src_path == cur->first)
- continue;
- if (version_changed(cur->first, resource_id(cur->first.c_str())))
- return true;
- }
- return false;
- }
- bool DataCompiler::should_ignore(const char* path)
- {
- for (u32 ii = 0, nn = vector::size(_globs); ii < nn; ++ii)
- {
- if (wildcmp(_globs[ii].c_str(), path))
- return true;
- }
- return false;
- }
- bool DataCompiler::compile(const char* data_dir, const char* platform)
- {
- const s64 time_start = time::now();
- FilesystemDisk data_fs(default_allocator());
- data_fs.set_prefix(data_dir);
- data_fs.create_directory("");
- data_fs.create_directory(CROWN_DATA_DIRECTORY);
- data_fs.create_directory(CROWN_TEMP_DIRECTORY);
- // Find the set of resources to be compiled
- Vector<DynamicString> to_compile(default_allocator());
- auto cur = hash_map::begin(_source_index._paths);
- auto end = hash_map::end(_source_index._paths);
- for (; cur != end; ++cur)
- {
- HASH_MAP_SKIP_HOLE(_source_index._paths, cur);
- const DynamicString& src_path = cur->first;
- const char* filename = src_path.c_str();
- const char* type = path::extension(filename);
- if (should_ignore(filename))
- continue;
- if (type == NULL || !can_compile(type))
- {
- loge(DATA_COMPILER, "Unknown resource file: '%s'", filename);
- loge(DATA_COMPILER, "Append matching pattern to " CROWN_DATAIGNORE " to ignore it");
- continue;
- }
- ResourceId id = resource_id(filename);
- TempAllocator256 ta;
- DynamicString path(ta);
- destination_path(path, id);
- u64 dst_mtime = 0;
- dst_mtime = hash_map::get(_data_mtimes, id, dst_mtime);
- Stat src_stat;
- src_stat = hash_map::get(_source_index._paths, src_path, src_stat);
- bool source_never_compiled_before = hash_map::has(_data_index, id) == false;
- bool source_has_been_changed = src_stat.mtime > dst_mtime;
- bool source_dependency_changed = dependency_changed(src_path, id, dst_mtime);
- bool data_version_changed = data_version_stored(type) != data_version(type);
- bool data_version_dependency_changed = version_changed(src_path, id);
- if (source_never_compiled_before
- || source_has_been_changed
- || source_dependency_changed
- || data_version_changed
- || data_version_dependency_changed
- )
- {
- vector::push_back(to_compile, src_path);
- }
- }
- // Sort to_compile so that ".package" resources get compiled last
- std::sort(vector::begin(to_compile), vector::end(to_compile), [](const DynamicString& resource_a, const DynamicString& resource_b)
- {
- #define PACKAGE ".package"
- if ( resource_a.has_suffix(PACKAGE) && !resource_b.has_suffix(PACKAGE))
- return false;
- if (!resource_a.has_suffix(PACKAGE) && resource_b.has_suffix(PACKAGE))
- return true;
- return resource_a < resource_b;
- #undef PACKAGE
- });
- bool success = true;
- // Compile all changed resources
- for (u32 i = 0; i < vector::size(to_compile); ++i)
- {
- const DynamicString& src_path = to_compile[i];
- const char* filename = src_path.c_str();
- const char* type = path::extension(filename);
- if (type == NULL)
- continue;
- TempAllocator1024 ta;
- DynamicString path(ta);
- // Build destination file path
- ResourceId id = resource_id(filename);
- destination_path(path, id);
- logi(DATA_COMPILER, "%s", src_path.c_str());
- // Compile data
- ResourceTypeData rtd;
- rtd.version = 0;
- rtd.compiler = NULL;
- DynamicString type_str(ta);
- type_str = type;
- rtd = hash_map::get(_compilers, type_str, rtd);
- {
- // Reset dependencies and references since data could not depend
- // anymore on any of those.
- HashMap<DynamicString, u32> dependencies_deffault(default_allocator());
- hash_map::clear(hash_map::get(_data_dependencies, id, dependencies_deffault));
- HashMap<DynamicString, u32> requirements_deffault(default_allocator());
- hash_map::clear(hash_map::get(_data_requirements, id, requirements_deffault));
- // Compile data
- Buffer output(default_allocator());
- CompileOptions opts(*this, data_fs, id, src_path, output, platform);
- success = rtd.compiler(opts) == 0;
- if (success)
- {
- File* outf = data_fs.open(path.c_str(), FileOpenMode::WRITE);
- u32 size = array::size(output);
- u32 written = outf->write(array::begin(output), size);
- data_fs.close(*outf);
- success = size == written;
- }
- }
- if (success)
- {
- hash_map::set(_data_index, id, src_path);
- hash_map::set(_data_versions, type_str, rtd.version);
- hash_map::set(_data_mtimes, id, data_fs.last_modified_time(path.c_str()));
- }
- else
- {
- loge(DATA_COMPILER, "Failed to compile data");
- break;
- }
- }
- if (success)
- {
- if (vector::size(to_compile))
- logi(DATA_COMPILER, "Compiled data in %.2fs", time::seconds(time::now() - time_start));
- else
- logi(DATA_COMPILER, "Data is up to date");
- }
- return success;
- }
- void DataCompiler::register_compiler(const char* type, u32 version, CompileFunction compiler)
- {
- TempAllocator64 ta;
- DynamicString type_str(ta);
- type_str = type;
- CE_ASSERT(!hash_map::has(_compilers, type_str), "Type already registered");
- CE_ENSURE(NULL != compiler);
- ResourceTypeData rtd;
- rtd.version = version;
- rtd.compiler = compiler;
- hash_map::set(_compilers, type_str, rtd);
- }
- u32 DataCompiler::data_version(const char* type)
- {
- TempAllocator64 ta;
- DynamicString type_str(ta);
- type_str = type;
- ResourceTypeData rtd;
- rtd.version = COMPILER_NOT_FOUND;
- rtd.compiler = NULL;
- return hash_map::get(_compilers, type_str, rtd).version;
- }
- u32 DataCompiler::data_version_stored(const char* type)
- {
- TempAllocator256 ta;
- DynamicString ds(ta);
- ds = type;
- u32 version = UINT32_MAX;
- return hash_map::get(_data_versions, ds, version);
- }
- void DataCompiler::add_dependency_internal(HashMap<StringId64, HashMap<DynamicString, u32> >& dependencies, ResourceId id, const char* dependency)
- {
- HashMap<DynamicString, u32> deps_deffault(default_allocator());
- if (hash_map::has(dependencies, id))
- {
- HashMap<DynamicString, u32>& deps = hash_map::get(dependencies, id, deps_deffault);
- TempAllocator256 ta;
- DynamicString dependency_ds(ta);
- dependency_ds = dependency;
- hash_map::set(deps, dependency_ds, 0u);
- }
- else
- {
- TempAllocator256 ta;
- DynamicString dependency_ds(ta);
- dependency_ds = dependency;
- hash_map::set(deps_deffault, dependency_ds, 0u);
- hash_map::set(dependencies, id, deps_deffault);
- }
- }
- void DataCompiler::add_dependency(ResourceId id, const char* dependency)
- {
- add_dependency_internal(_data_dependencies, id, dependency);
- }
- void DataCompiler::add_requirement(ResourceId id, const char* requirement)
- {
- add_dependency_internal(_data_requirements, id, requirement);
- }
- bool DataCompiler::can_compile(const char* type)
- {
- TempAllocator64 ta;
- DynamicString type_str(ta);
- type_str = type;
- return hash_map::has(_compilers, type_str);
- }
- void DataCompiler::error(const char* msg, va_list args)
- {
- vloge(DATA_COMPILER, msg, args);
- }
- void DataCompiler::file_monitor_callback(FileMonitorEvent::Enum fme, bool is_dir, const char* path, const char* path_renamed)
- {
- TempAllocator512 ta;
- DynamicString source_dir(ta);
- DynamicString resource_name(ta);
- // Find source directory by matching mapped
- // directory prefix with `path`.
- auto cur = hash_map::begin(_source_dirs);
- auto end = hash_map::end(_source_dirs);
- for (; cur != end; ++cur)
- {
- HASH_MAP_SKIP_HOLE(_source_dirs, cur);
- path::join(source_dir, cur->second.c_str(), cur->first.c_str());
- if (str_has_prefix(path, source_dir.c_str()))
- break;
- }
- if (cur != end)
- {
- // All events received must refer to directories
- // mapped with map_source_dir().
- const char* filename = &path[source_dir.length()+1];
- path::join(resource_name, cur->first.c_str(), filename);
- #if 0
- logi(DATA_COMPILER, "path : %s", path);
- logi(DATA_COMPILER, "source_dir : %s", source_dir.c_str());
- logi(DATA_COMPILER, "resource_name: %s", resource_name.c_str());
- #endif
- switch (fme)
- {
- case FileMonitorEvent::CREATED:
- if (!is_dir)
- add_file(resource_name.c_str());
- else
- add_tree(resource_name.c_str());
- break;
- case FileMonitorEvent::DELETED:
- if (!is_dir)
- remove_file(resource_name.c_str());
- else
- remove_tree(resource_name.c_str());
- break;
- case FileMonitorEvent::RENAMED:
- {
- DynamicString resource_name_renamed(ta);
- path::join(resource_name_renamed, cur->first.c_str(), &path_renamed[source_dir.length()+1]);
- if (!is_dir)
- {
- remove_file(resource_name.c_str());
- add_file(resource_name_renamed.c_str());
- }
- else
- {
- remove_tree(resource_name.c_str());
- add_tree(resource_name_renamed.c_str());
- }
- }
- break;
- case FileMonitorEvent::CHANGED:
- if (!is_dir)
- {
- FilesystemDisk fs(default_allocator());
- fs.set_prefix(source_dir.c_str());
- Stat stat;
- stat = fs.stat(filename);
- hash_map::set(_source_index._paths, resource_name, stat);
- }
- break;
- default:
- CE_ASSERT(false, "Unknown FileMonitorEvent: %d", fme);
- break;
- }
- }
- }
- void DataCompiler::file_monitor_callback(void* thiz, FileMonitorEvent::Enum fme, bool is_dir, const char* path_original, const char* path_modified)
- {
- ((DataCompiler*)thiz)->file_monitor_callback(fme, is_dir, path_original, path_modified);
- }
- int main_data_compiler(const DeviceOptions& opts)
- {
- #if CROWN_PLATFORM_POSIX
- struct sigaction old_SIGINT;
- struct sigaction act;
- act.sa_handler = [](int /*signum*/) { _quit = true; };
- sigemptyset(&act.sa_mask);
- act.sa_flags = 0;
- sigaction(SIGINT, NULL, &old_SIGINT);
- sigaction(SIGINT, &act, NULL);
- #endif // CROWN_PLATFORM_POSIX
- console_server_globals::init();
- console_server()->listen(CROWN_DEFAULT_COMPILER_PORT, opts._wait_console);
- namespace cor = config_resource_internal;
- namespace ftr = font_resource_internal;
- namespace lur = lua_resource_internal;
- namespace lvr = level_resource_internal;
- namespace mhr = mesh_resource_internal;
- namespace mtr = material_resource_internal;
- namespace pcr = physics_config_resource_internal;
- namespace phr = physics_resource_internal;
- namespace pkr = package_resource_internal;
- namespace sar = sprite_animation_resource_internal;
- namespace sdr = sound_resource_internal;
- namespace shr = shader_resource_internal;
- namespace smr = state_machine_internal;
- namespace spr = sprite_resource_internal;
- namespace txr = texture_resource_internal;
- namespace utr = unit_resource_internal;
- DataCompiler* dc = CE_NEW(default_allocator(), DataCompiler)(opts, *console_server());
- dc->register_compiler("config", RESOURCE_VERSION_CONFIG, cor::compile);
- dc->register_compiler("font", RESOURCE_VERSION_FONT, ftr::compile);
- dc->register_compiler("level", RESOURCE_VERSION_LEVEL, lvr::compile);
- dc->register_compiler("material", RESOURCE_VERSION_MATERIAL, mtr::compile);
- dc->register_compiler("mesh", RESOURCE_VERSION_MESH, mhr::compile);
- dc->register_compiler("package", RESOURCE_VERSION_PACKAGE, pkr::compile);
- dc->register_compiler("physics_config", RESOURCE_VERSION_PHYSICS_CONFIG, pcr::compile);
- dc->register_compiler("lua", RESOURCE_VERSION_SCRIPT, lur::compile);
- dc->register_compiler("shader", RESOURCE_VERSION_SHADER, shr::compile);
- dc->register_compiler("sound", RESOURCE_VERSION_SOUND, sdr::compile);
- dc->register_compiler("sprite", RESOURCE_VERSION_SPRITE, spr::compile);
- dc->register_compiler("sprite_animation", RESOURCE_VERSION_SPRITE_ANIMATION, sar::compile);
- dc->register_compiler("state_machine", RESOURCE_VERSION_STATE_MACHINE, smr::compile);
- dc->register_compiler("texture", RESOURCE_VERSION_TEXTURE, txr::compile);
- dc->register_compiler("unit", RESOURCE_VERSION_UNIT, utr::compile);
- // Add ignore globs
- dc->add_ignore_glob("*.bak");
- dc->add_ignore_glob("*.dds");
- dc->add_ignore_glob("*.importer_settings");
- dc->add_ignore_glob("*.ktx");
- dc->add_ignore_glob("*.ogg");
- dc->add_ignore_glob("*.png");
- dc->add_ignore_glob("*.pvr");
- dc->add_ignore_glob("*.swn"); // VIM swap file.
- dc->add_ignore_glob("*.swo"); // VIM swap file.
- dc->add_ignore_glob("*.swp"); // VIM swap file.
- dc->add_ignore_glob("*.tga");
- dc->add_ignore_glob("*.tmp");
- dc->add_ignore_glob("*.wav");
- dc->add_ignore_glob("*~");
- dc->add_ignore_glob(".*");
- dc->map_source_dir("", opts._source_dir.c_str());
- if (opts._map_source_dir_name)
- {
- dc->map_source_dir(opts._map_source_dir_name
- , opts._map_source_dir_prefix.c_str()
- );
- }
- dc->scan_and_restore(opts._data_dir.c_str());
- bool success = true;
- if (opts._server)
- {
- while (!_quit)
- {
- console_server()->update();
- os::sleep(60);
- }
- }
- else
- {
- success = dc->compile(opts._data_dir.c_str(), opts._platform);
- }
- dc->save(opts._data_dir.c_str());
- CE_DELETE(default_allocator(), dc);
- console_server_globals::shutdown();
- #if CROWN_PLATFORM_POSIX
- // Restore original handler
- sigaction(SIGINT, &old_SIGINT, NULL);
- #endif // CROWN_PLATFORM_POSIX
- return success ? EXIT_SUCCESS : EXIT_FAILURE;
- }
- } // namespace crown
- #endif // CROWN_CAN_COMPILE
|