diff --git a/MILESTONES.md b/MILESTONES.md index c7068a57..aa24537c 100644 --- a/MILESTONES.md +++ b/MILESTONES.md @@ -1,15 +1,9 @@ General overview of upcoming features. For past releases please check in [here](./docs/releases/). -## 0.1.1-alpha - -- [x] Complete the build/install process -- [x] Docker image -- [x] Flatpak building pipeline -- [x] Prepare pipeline for github actions - ## 0.1.3 +- [x] Move all components to the revised codegen format - [ ] New getters and setters design (and updated schemas). - [ ] Implement flex, grid, scroll & pack via codegen. - [ ] Tidy up the XML caching @@ -18,7 +12,6 @@ For past releases please check in [here](./docs/releases/). ## 0.1.5 -- [x] Move all components to the codegen format - [x] Support for script modules - [ ] Full support for getters and setters - [x] Final implementation of the static xml builder (now external dependency) diff --git a/RELEASE.md b/RELEASE.md index 5860eb60..d0428bc8 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -1,64 +1,29 @@ -## v0.1.1-alpha +## v0.1.3-alpha -This first alpha release is not meant to be usable in real-world scenarios, and documentation will not be there yet for the most part. -The main objective is to implement & test most of the pipelines needed to: +This second alpha release is still not usable in real-world scenarios, yet some simple and somewhat useful demos should be achievable. -- build `vs` -- install it -- the generation of its flatpak image +The main objectives for this release are: -Still, `vs` is somewhat usable as a technical preview. -For now, the building pipeline is not well documented, and it can be hard to replicate. -Also, installing it on most distributions might cause issues with your past or future dependencies. -You should be able to try it out via flatpak and the docker image provided by @andy5995. -I am seeking feedback, so you are very welcome to test it along! - -For reference, you can check what is the [intended scope](./README.md) of this project in terms of future developments and expected features. -The [milestone](./MILESTONES.md) page also contains some related information. +- To improve the build system moving it to a separate repo. +- To introduce a wider range of fltk widgets. +- Complete the revised code-generation of widgets +- Better serialization/de-serialization of XML props & revised getters/setters ### What's new? This is what has been implemented so far (and some missing feature for context): -- Basic CLI features for the command `vs`. These are not expected to change for quite a while. -- Code generation infrastructure for fltk components and derived schemas. -- Infrastructure to handle the user sqlite database with proper schemas and migration. -- A small set of components from fltk being exposed, like buttons, labels, windows etc. -- Several features of `vs` exposed as XML elements: - - [x] `mixin` - - [x] embedded `script` - - [ ] embedded `policies` - - [x] `namespace` - - [x] `import` - - [x] `use` - - [ ] caching directives - - [ ] dynamic data -- Basic XML builder (no full caching, no multithreading) -- Templating engine for vs components. Integration is complete, but there is ongoing work tracked in a [separate repo](https://github.com/KaruroChori/vs-templ) -- Embedded scripts: - - Support for `c` via tcc - - [x] Modules - - [x] Callbacks - - [x] Props - - [ ] Computed - - [x] Dispatcher - - [ ] Setters/getters - - [ ] FFI to external libraries - - [x] Debug interface - - Support for `js` via quickjs - - [ ] Modules - - [x] Callbacks - - [x] Props - - [ ] Computed - - [ ] Dispatcher - - [ ] Setters/getters - - [ ] FFI to external libraries - - [x] Debug interface -- Very early and limited c bindings for `vs.fltk`, mostly for testing purposes. -- A simple in-memory caching for files, compiled scripts & parsed XML trees. - The more complex sqlite cache is yet to be implemented in code. -- An early (partial) implementation of policies and coarse-grained flags as a temporary stopgap. -- Install process mostly implemented in meson (the pre-build codegen step will stay in typescript). -- Functional flatpak build -- A docker image for development -- Initial CI integrations +- [ ] Improvements to the build system + + - [ ] migration of distribution-related features to [external repo]() + - [ ] fix linking to follow https://github.com/KaruroChori/vs-fltk/issues/63 + - [ ] debian and arch packages are now supported + - [ ] install script can be now be safely used + +- [ ] New widgets & improvements + +- [ ] Updated code-gen + +- [ ] Updated core features + - [x] Removed most of the globals + - [x] Reworked `ui_tree` interface to be more generic and not only viable for xml trees. diff --git a/TODO.md b/TODO.md index e69de29b..43f52f37 100644 --- a/TODO.md +++ b/TODO.md @@ -0,0 +1,3 @@ +- [ ] Restore vs_debug +- [ ] Allow http only withou libcurl via https://github.com/lazy-eggplant/libhttp + diff --git a/bindings/native/include/stub.h b/bindings/native/include/stub.h index 6c58afa3..14e2b438 100644 --- a/bindings/native/include/stub.h +++ b/bindings/native/include/stub.h @@ -1,5 +1,4 @@ #pragma once #include "vs.h" -char* itoa(int value, char* result, int base); void* vs_set_env(void* ptr); \ No newline at end of file diff --git a/bindings/native/include/vs.h b/bindings/native/include/vs.h index f5fcddc1..18d500f6 100644 --- a/bindings/native/include/vs.h +++ b/bindings/native/include/vs.h @@ -104,4 +104,7 @@ extern void vs_debug(const char* key, const char* value); #define $cb(x) void* __EXPORT_CB__##x = x; #define $plotter(x) void* __EXPORT_DRW_##x = x; #define $field vs_field_t MAKE_UNIQUE_VARIABLE_NAME(__EXPORT_FIELD_)= -#define $fn(x) void* __EXPORT_UKN_##x = x; \ No newline at end of file +#define $fn(x) void* __EXPORT_UKN_##x = x; + +//Extra functions +char* itoa(int value, char* result, int base); diff --git a/bindings/native/src/stub.c b/bindings/native/src/stub.c index eb8798b1..aee1f313 100644 --- a/bindings/native/src/stub.c +++ b/bindings/native/src/stub.c @@ -1,31 +1,5 @@ #include -char* itoa(int value, char* result, int base) { - // check that the base if valid - if (base < 2 || base > 36) { *result = '\0'; return result; } - - char* ptr = result, *ptr1 = result, tmp_char; - int tmp_value; - - do { - tmp_value = value; - value /= base; - *ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz" [35 + (tmp_value - value * base)]; - } while ( value ); - - // Apply negative sign - if (tmp_value < 0) *ptr++ = '-'; - *ptr-- = '\0'; - - // Reverse the string - while(ptr1 < ptr) { - tmp_char = *ptr; - *ptr--= *ptr1; - *ptr1++ = tmp_char; - } - return result; -} - node_t vs_self = 0; void* vs_set_env(void* ptr){ diff --git a/commons/schemas/json-component.schema.json b/commons/schemas/json-component.schema.json index 4fa53dff..7862690f 100644 --- a/commons/schemas/json-component.schema.json +++ b/commons/schemas/json-component.schema.json @@ -6,20 +6,24 @@ "type": "string" }, "ns": { + "description": "Namespace for this component, for example `fl` in case of fltk wrappers.", "type": "string" }, "name": { "type": "string" }, - "usable": { + "exposed": { "default": true, + "description": "If true, this widget will be usable from XML and shown in the documentation, otherwise it is not exported.", "type": "boolean" }, "description": { + "description": "While optional, this field is extremely important for exposed widgets", "type": "string" }, "use_main_header": { "default": null, + "description": "If set, no class definition will be generated. It will instread load one already present. Used to track and document widgets which are not fully autogenerated.", "anyOf": [ { "type": "null" @@ -30,12 +34,21 @@ ] }, "headers": { + "description": "List of header files used by the widget in its public header file.", + "type": "array", + "items": { + "type": "string" + } + }, + "private_headers": { + "description": "List of header files used by the widget in its generated cpp file.", "type": "array", "items": { "type": "string" } }, "type": { + "description": "Type of frame an instance of this widget class will default top.", "anyOf": [ { "const": "leaf", @@ -65,6 +78,7 @@ "properties": { "extends": { "default": null, + "description": "If set, a class to inherit from, usually related to the fltk widget.", "anyOf": [ { "type": "null" @@ -74,7 +88,8 @@ } ] }, - "props_tail": { + "set_tail": { + "description": "If specified, what to do if a getter is not matched.", "anyOf": [ { "type": "null" @@ -84,7 +99,8 @@ } ] }, - "computed_tail": { + "get_tail": { + "description": "If specified, what to do if a setter is not matched.", "anyOf": [ { "type": "null" @@ -101,6 +117,7 @@ }, "extends": { "default": null, + "description": "If set, a vs widget to inherit from (not a c++ class name)", "anyOf": [ { "type": "null" @@ -110,18 +127,19 @@ } ] }, - "skip_props": { + "skip_fields": { "default": [], - "description": "Properties to be matched but ignored", + "description": "Properties to be matched when set, but ignored.", "type": "array", "items": { "type": "string" } }, - "props": { + "fields": { "type": "object", "patternProperties": { "^(.*)$": { + "additionalProperties": false, "type": "object", "properties": { "type": { @@ -141,11 +159,11 @@ "type": "string" }, { - "const": "string", + "const": "path", "type": "string" }, { - "const": "path", + "const": "string", "type": "string" }, { @@ -169,8 +187,8 @@ "subtype": { "type": "string" }, - "code": { - "default": null, + "parse": { + "description": "From string to object. Default based on `type` (and `subtype`)", "anyOf": [ { "type": "string" @@ -180,78 +198,19 @@ } ] }, - "description": { - "type": "string" - }, - "alias": { - "description": "alias names", - "default": [], - "type": "array", - "items": { - "type": "string" - } - } - }, - "required": [ - "type", - "code" - ] - } - } - }, - "computed": { - "type": "object", - "patternProperties": { - "^(.*)$": { - "type": "object", - "properties": { - "type": { - "description": "type", - "default": "string", + "setter": { + "description": "Operations on setting. If undefined, this field is only available as a computed value. If null use default", "anyOf": [ { - "const": "flag", - "type": "string" - }, - { - "const": "enum", - "type": "string" - }, - { - "const": "raw", - "type": "string" - }, - { - "const": "string", "type": "string" }, { - "const": "path", - "type": "string" - }, - { - "const": "color", - "type": "string" - }, - { - "const": "scalar-1", - "type": "string" - }, - { - "const": "scalar-2", - "type": "string" - }, - { - "const": "scalar-4", - "type": "string" + "type": "null" } ] }, - "subtype": { - "type": "string" - }, - "code": { - "default": null, + "getter": { + "description": "Implementation on how to extract value from object. If undefined the field is not observable. If null use default", "anyOf": [ { "type": "string" @@ -264,6 +223,11 @@ "description": { "type": "string" }, + "semantic": { + "default": false, + "description": "If true, this field has a strong semantic meaning. Used for semantic serialization of a document.", + "type": "boolean" + }, "alias": { "description": "alias names", "default": [], @@ -274,8 +238,7 @@ } }, "required": [ - "type", - "code" + "type" ] } } @@ -286,7 +249,6 @@ "type", "codegen", "extends", - "props", - "computed" + "fields" ] } \ No newline at end of file diff --git a/include/fetcher.hpp b/include/fetcher.hpp index d9fa0f82..71750134 100644 --- a/include/fetcher.hpp +++ b/include/fetcher.hpp @@ -52,7 +52,7 @@ std::tuple the return code, a buffer and the new base path derived from src */ -std::tuple fetcher(resolve_path& base, resolve_path::from_t scope,const char* src, bool promote=false, bool preserve=false); +std::tuple fetcher(global_ctx_t& globals, resolve_path& base, resolve_path::from_t scope,const char* src, bool promote=false, bool preserve=false); /** * @brief Fetch a resource, convert it in an xml tree and store in in cache (or get it if already there) @@ -65,6 +65,6 @@ std::tuple fetcher(re * @param preserve if true, this entry should be preserved after the build process is done * @return std::tuple the return code, a buffer and the new base path derived from src */ -std::tuple fetcher_xml(resolve_path& base, resolve_path::from_t scope,const char* src, bool promote=false, bool preserve=false); +std::tuple fetcher_xml(global_ctx_t& globals, resolve_path& base, resolve_path::from_t scope,const char* src, bool promote=false, bool preserve=false); } \ No newline at end of file diff --git a/include/globals.hpp b/include/globals.hpp index 375cfe21..a51bb270 100644 --- a/include/globals.hpp +++ b/include/globals.hpp @@ -11,12 +11,12 @@ namespace vs{ -namespace globals{ +/* extern app_env_t env; //Computed basic environment based on user config & shell env extern path_env_t path_env; //Computed paths - //extern policies_t policy; +//extern policies_t policy; extern js_rt_t js_rt; extern cache::mem_storage_t mem_storage; @@ -24,17 +24,25 @@ extern cache::mem_storage_t mem_storage; //extern cache::res_storage_t res_storage; //extern cache::secrets_t secrets; -struct vs_test_debug_t{ - FILE* fd = nullptr; - vs_test_debug_t(); - ~vs_test_debug_t(); - - void operator()(const char* field, const char* value); -}; - extern vs_test_debug_t debug; extern field_models_t value_models; +*/ + + +/** + * @brief Global context structure + * + */ +struct global_ctx_t{ + app_env_t env; + path_env_t path_env; + + js_rt_t js_rt; + cache::mem_storage_t mem_storage; + vs_test_debug_t debug; + field_models_t value_models; +}; + -} } \ No newline at end of file diff --git a/include/loader.hpp b/include/loader.hpp index 2c1fce13..982728bd 100644 --- a/include/loader.hpp +++ b/include/loader.hpp @@ -4,12 +4,15 @@ Glue logic exposed to the final application to avoid exposing too many libraries. */ +#include "globals.hpp" + class ui_tree_xml; namespace vs{ struct app_loader{ ui_tree_xml* root; + - app_loader(const char* profile, const char* path); + app_loader(global_ctx_t& globals, const char* profile, const char* path); ~app_loader(); int run(); int test(); diff --git a/include/pipelines/quickjs-js.hpp b/include/pipelines/quickjs-js.hpp index 76b12289..268189b6 100644 --- a/include/pipelines/quickjs-js.hpp +++ b/include/pipelines/quickjs-js.hpp @@ -1,5 +1,8 @@ #pragma once +#include "globals.hpp" +#if VS_USE_QJS + #include #include #include @@ -39,13 +42,16 @@ struct quickjs_t{ } }; -extern std::shared_ptr qjs_js_pipeline(bool is_runtime, vs::ui_base* obj, const char* src, void* ctx, void(*error_fn)(void*,const char*), const char *link_with); +extern std::shared_ptr qjs_js_pipeline(global_ctx_t& globals, bool is_runtime, vs::ui_base* obj, const char* src, void* ctx, void(*error_fn)(void*,const char*), const char *link_with); extern std::shared_ptr> qjs_js_pipeline_apply(const std::shared_ptr& script,vs::ui_base* obj,void* ctx,void(*register_fn)(void*,const char*, const char*)); -inline std::shared_ptr qjs_js_pipeline_xml(bool is_runtime, vs::ui_base* obj, pugi::xml_node& ctx, const char *link_with){ - return qjs_js_pipeline(is_runtime,obj,ctx.text().as_string(),&ctx,(void(*)(void*,const char*))qjs_error_func_xml,link_with); +inline std::shared_ptr qjs_js_pipeline_xml(global_ctx_t& globals, bool is_runtime, vs::ui_base* obj, pugi::xml_node& ctx, const char *link_with){ + return qjs_js_pipeline(globals,is_runtime,obj,ctx.text().as_string(),&ctx,(void(*)(void*,const char*))qjs_error_func_xml,link_with); } } -} \ No newline at end of file +} + + +#endif \ No newline at end of file diff --git a/include/pipelines/tcc-c.hpp b/include/pipelines/tcc-c.hpp index 9997ec01..b0ee35e8 100644 --- a/include/pipelines/tcc-c.hpp +++ b/include/pipelines/tcc-c.hpp @@ -1,5 +1,9 @@ #pragma once +#include "globals.hpp" +#if VS_USE_TCC + + #include #include #include @@ -27,12 +31,14 @@ extern void tcc_log_symbol_func_xml(const pugi::xml_node& env, const char * msg, * @param link_with path of a library to be linked against (exposing the standard vs interface) * @return std::shared_ptr */ -extern std::shared_ptr tcc_c_pipeline(bool is_runtime, ui_base* obj, const char* src, void* ctx, void(*error_fn)(void*,const char*), bool compact, const char *link_with); +extern std::shared_ptr tcc_c_pipeline(global_ctx_t& globals, bool is_runtime, ui_base* obj, const char* src, void* ctx, void(*error_fn)(void*,const char*), bool compact, const char *link_with); extern std::shared_ptr> tcc_c_pipeline_apply(const std::shared_ptr& script,vs::ui_base* obj,void* ctx,void(*register_fn)(void*,const char*, const char*)); -inline std::shared_ptr tcc_c_pipeline_xml(bool is_runtime, vs::ui_base* obj, pugi::xml_node& ctx, bool compact, const char *link_with){ - return tcc_c_pipeline(is_runtime,obj,ctx.text().as_string(),&ctx,(void(*)(void*,const char*))tcc_error_func_xml, compact, link_with); +inline std::shared_ptr tcc_c_pipeline_xml(global_ctx_t& globals, bool is_runtime, vs::ui_base* obj, pugi::xml_node& ctx, bool compact, const char *link_with){ + return tcc_c_pipeline(globals,is_runtime,obj,ctx.text().as_string(),&ctx,(void(*)(void*,const char*))tcc_error_func_xml, compact, link_with); } } -} \ No newline at end of file +} + +#endif \ No newline at end of file diff --git a/include/ui-tree.hpp b/include/ui-tree.hpp index 1fd54d78..9b1995aa 100644 --- a/include/ui-tree.hpp +++ b/include/ui-tree.hpp @@ -1,5 +1,6 @@ #pragma once +#include "globals.hpp" #include "ui-frame.hpp" #include #include @@ -14,15 +15,15 @@ struct ui_tree { //Define the embedded mode supported. frame_mode_t mode = frame_mode_t::AUTO; - ui_tree* parent = nullptr; //Set if there is an explict owner of this root, for example a viewport. - + global_ctx_t& globals; //Just for book-keeping + ui_tree* parent = nullptr; //Set if there is an explict owner of this root, for example a viewport/app. ui_base* caller_ui_node=nullptr; //Element from a parent tree calling me ui_base* root = nullptr; //Base element of this tree - policies_t policies; //Computed policies for this tree - scoped_rpath_t local; //Full path for the location of this component. - scoped_rpath_t fullname; //Full path for the location of this component. + policies_t policies; //Computed policies for this tree + scoped_rpath_t local; //Full path for the location of this component. + scoped_rpath_t fullname; //Full path for the location of this component. size_t local_unique_counter = 0; // Globals @@ -47,6 +48,8 @@ struct ui_tree { virtual void cleanup(); virtual int runtime_testsuite(); + inline ui_tree(global_ctx_t& g, ui_tree* parent, ui_base* caller_ui_node):globals(g){this->parent=parent;this->caller_ui_node=caller_ui_node;} + }; diff --git a/include/ui-tree.xml.hpp b/include/ui-tree.xml.hpp index 19edf448..bd769c66 100644 --- a/include/ui-tree.xml.hpp +++ b/include/ui-tree.xml.hpp @@ -25,7 +25,7 @@ struct ui_tree_xml : ui_tree { ns_strings strings; # endif - const pugi::xml_node *caller_node = nullptr; + const pugi::xml_node *caller_xml_node = nullptr; pugi::xml_document doc; //Handle of the xml parser @@ -49,8 +49,9 @@ struct ui_tree_xml : ui_tree { int build(); - int load(const char* file, type_t type, const pugi::xml_node* caller_node=nullptr, ui_base* caller_ui_node=nullptr, const scoped_rpath_t* caller_path=nullptr, const policies_t& base_policies=globals::env.computed_policies); + int load(const char* file, type_t type); + inline ui_tree_xml(global_ctx_t& g, ui_tree* parent, ui_base* caller_ui_node, const pugi::xml_node* caller_xml_node):ui_tree(g,parent,caller_ui_node){this->caller_xml_node=caller_xml_node;} virtual ~ui_tree_xml(); virtual void cleanup(); virtual int runtime_testsuite(){if(this->root!=nullptr)return this->root->all_tests();return 0;} diff --git a/include/utils/env.hpp b/include/utils/env.hpp index 8874c6c5..75a1c827 100644 --- a/include/utils/env.hpp +++ b/include/utils/env.hpp @@ -1,5 +1,6 @@ #pragma once #include + #include /** @@ -13,7 +14,10 @@ namespace vs{ -path_env_t mk_env(const char* arg0, const char* arg1); + +struct global_ctx_t; + +path_env_t mk_env(global_ctx_t& globals, const char* arg0, const char* arg1); struct js_rt_t{ void* rt; @@ -22,6 +26,14 @@ struct js_rt_t{ void* operator()(); }; +struct vs_test_debug_t{ + FILE* fd = nullptr; + vs_test_debug_t(); + ~vs_test_debug_t(); + + void operator()(const char* field, const char* value); +}; + /** * @brief In case it is not configured already, it prepares the SQLITE file and all the pre-compiled queries needed. * diff --git a/include/utils/tcc-wrap.hpp b/include/utils/tcc-wrap.hpp index b4c87eff..4d7f8f39 100644 --- a/include/utils/tcc-wrap.hpp +++ b/include/utils/tcc-wrap.hpp @@ -1,5 +1,6 @@ #pragma once +#if VS_USE_TCC /* Utility library to simplify the usage of libtcc */ @@ -86,4 +87,6 @@ class tcc{ compileA->relocate(); return std::move(compileA); } -}; \ No newline at end of file +}; + +#endif \ No newline at end of file diff --git a/include/version.hpp b/include/version.hpp index 822be63d..ad8d2d43 100644 --- a/include/version.hpp +++ b/include/version.hpp @@ -1,6 +1,6 @@ #pragma once constexpr int vs_version_major=0; constexpr int vs_version_minor=1; -constexpr int vs_version_rev=1; +constexpr int vs_version_rev=2; inline const char* vs_version_tag="alpha"; -inline const char* vs_version(){return "0.1.1-alpha";} +inline const char* vs_version(){return "0.1.2-alpha";} diff --git a/meson.build b/meson.build index c22f6b78..762e6606 100644 --- a/meson.build +++ b/meson.build @@ -2,7 +2,7 @@ project( 'vs-fltk', ['c', 'cpp', 'swift'], # - version: '0.1.1-alpha', + version: '0.1.2-alpha', # meson_version: '>= 1.1', default_options: ['c_std=gnu23', 'cpp_std=gnu++23'], @@ -11,15 +11,47 @@ cmake = import('cmake') cc = meson.get_compiler('c') cxx = meson.get_compiler('cpp') - -need_pugixml=false -need_fltk=false + +need_pugixml = false +need_fltk = false + +# Full dependency list for vs_fltk + +vs_fltk_deps = [] + +# Process options + +if get_option('use_tcc') + add_global_arguments(['-DVS_USE_TCC'], language: ['cpp', 'c']) +endif + +if get_option('use_quickjs') + add_global_arguments(['-DVS_USE_QJS'], language: ['cpp', 'c']) +endif + +if get_option('use_wamr') + add_global_arguments(['-DVS_USE_WAMR'], language: ['cpp', 'c']) +endif + +if get_option('use_lua') and get_option('use_luajit') + error( + 'Only lua or luajit can be used. At least one must be disabled from the options', + ) +endif + +if get_option('use_lua') + add_global_arguments(['-DVS_USE_LUA'], language: ['cpp', 'c']) +endif + +if get_option('use_luajit') + add_global_arguments(['-DVS_USE_LUAJIT'], language: ['cpp', 'c']) +endif if get_option('use_system_pugixml') - pugixml_dep = dependency('pugixml', version : '>=1.14', required: false) + pugixml_dep = dependency('pugixml', version: '>=1.14', required: false) if pugixml_dep.found() == false need_pugixml = true - endif + endif endif if need_pugixml or get_option('use_system_pugixml') == false #Used by pugi to enable compact mode. I might want to disable execptions too. @@ -29,14 +61,14 @@ if need_pugixml or get_option('use_system_pugixml') == false endif if get_option('use_system_fltk') - libfltk_dep = dependency('fltk', version : '>=1.4', required: false) - libfltk_images_dep = dependency('fltk-images', version : '>=1.4', required: false) - libfltk_forms_dep = dependency('fltk-images', version : '>=1.4', required: false) - if libfltk_dep.found()==false or libfltk_images_dep.found()==false or libfltk_forms_dep.found()==false - need_fltk=true + libfltk_dep = dependency('fltk', version: '>=1.4', required: false) + libfltk_images_dep = dependency('fltk-images', version: '>=1.4', required: false) + libfltk_forms_dep = dependency('fltk-forms', version: '>=1.4', required: false) + if libfltk_dep.found() == false or libfltk_images_dep.found() == false or libfltk_forms_dep.found() == false + need_fltk = true endif endif -if need_fltk==true or get_option('use_system_fltk') == false +if need_fltk == true or get_option('use_system_fltk') == false opt_fltk = cmake.subproject_options() opt_fltk.add_cmake_defines( { @@ -60,26 +92,42 @@ if need_fltk==true or get_option('use_system_fltk') == false #libfltk_gl_dep = libfltk_proj.dependency(['fltk-gl']) endif -opt_wamr = cmake.subproject_options() -opt_wamr.add_cmake_defines( - { - 'WAMR_BUILD_INTERP': 1, - 'WAMR_BUILD_PLATFORM': target_machine.system(), - }, -) +if get_option('use_wamr') + opt_wamr = cmake.subproject_options() + opt_wamr.add_cmake_defines( + { + 'WAMR_BUILD_INTERP': 1, + 'WAMR_BUILD_PLATFORM': target_machine.system(), + }, + ) + wamr_proj = cmake.subproject('wamr', options: opt_wamr) + wamr_dep = wamr_proj.dependency('wamr') -wamr_proj = cmake.subproject('wamr', options: opt_wamr) -wamr_dep = wamr_proj.dependency('wamr') + vs_fltk_deps += wamr_dep -opt_quickjs = cmake.subproject_options() -opt_quickjs.add_cmake_defines( - { - 'CMAKE_POSITION_INDEPENDENT_CODE': true, - }, -) +endif -quickjs_proj = cmake.subproject('quickjs', options: opt_quickjs) -quickjs_dep = quickjs_proj.dependency('qjs') +if get_option('use_quickjs') + opt_quickjs = cmake.subproject_options() + opt_quickjs.add_cmake_defines( + { + 'CMAKE_POSITION_INDEPENDENT_CODE': true, + }, + ) + + quickjs_proj = cmake.subproject('quickjs', options: opt_quickjs) + quickjs_dep = quickjs_proj.dependency('qjs') + + vs_fltk_deps += quickjs_dep + +endif + +if get_option('use_tcc') + libtcc_proj = subproject('libtcc') + libtcc_dep = libtcc_proj.get_variable('libtcc_dep') + + vs_fltk_deps += libtcc_dep +endif md4c_proj = cmake.subproject('md4c') md4c_dep = md4c_proj.dependency('md4c') @@ -104,9 +152,6 @@ vs_templ_dep = vs_templ_proj.get_variable('vs_templ_dep') treesitter_proj = subproject('tree-sitter') treesitter_dep = treesitter_proj.get_variable('tree_sitter_dep') -libtcc_proj = subproject('libtcc') -libtcc_dep = libtcc_proj.get_variable('libtcc_dep') - #uv_proj = subproject('libuv') #uv_dep = uv_proj.get_variable('libuv_dep') @@ -132,14 +177,27 @@ sqlite_dep = dependency( default_options: 'default_library=static', ) -#TODO handle the case in which libcurl does not exist. Missing is fine, just no https/http prefix for paths. -curl = dependency('libcurl', required: false) +if get_option('use_networking_curl') and get_option('use_networking_simple') + error('Only one networking solution can be selected between curl and simple') +endif + +if get_option('use_networking_curl') + curl = dependency('libcurl', required: false) +endif + +#TODO: support networking_simple +if get_option('use_networking_simple') + libhttp_proj = subproject('libhttp') + libhttp_dep = libhttp_proj.get_variable('libhttp_dep') + + vs_fltk_deps += libhttp_dep +endif include_dirs = include_directories(['./include']) # Prepare `vs` -vs_fltk_deps = [ +vs_fltk_deps += [ libfltk_dep, libfltk_images_dep, libfltk_forms_dep, @@ -152,8 +210,6 @@ vs_fltk_deps = [ #uv_dep, The one provided with the system if present should be fine for our tasks hashlib_dep, - libtcc_dep, - quickjs_dep, sqlite_dep, sqlitecpp_dep, diff --git a/meson.options b/meson.options index 4d15d92f..bb25f1da 100644 --- a/meson.options +++ b/meson.options @@ -1,6 +1,7 @@ option('tests', type: 'boolean', value: true) -option('use_curl', type: 'boolean', value: true) +option('use_networking_curl', type: 'boolean', value: true) +option('use_networking_simple', type: 'boolean', value: false) option('use_tcc', type: 'boolean', value: true) option('use_quickjs', type: 'boolean', value: true) @@ -8,7 +9,7 @@ option('use_wamr', type: 'boolean', value: true) # Only one between lua and luajit should be enabled option('use_lua', type: 'boolean', value: true) -option('use_luajit', type: 'boolean', value: true) +option('use_luajit', type: 'boolean', value: false) # If present, else build it manually option('use_system_fltk', type: 'boolean', value: true) diff --git a/package.json b/package.json index 1887aed8..35f87b0c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vs-fltk", "description": "Build system for `vs`, a sfc framework for native UI frontends", - "version": "0.1.1-alpha", + "version": "0.1.2-alpha", "license": "UNLICENSED", "scripts": { "codegen": "bun ./scripts/codegen/index.ts", diff --git a/schemas/components/fl:base.json b/schemas/components/fl:base.json index ff89c15f..ea95b7e0 100644 --- a/schemas/components/fl:base.json +++ b/schemas/components/fl:base.json @@ -1,170 +1,160 @@ { "$schema": "../../commons/schemas/json-component.schema.json", "description": "Base element to provide basic props/computed for all those which are derived from `fltk_widget`", - "usable": false, + "exposed": false, "type": "node", "headers": ["FL/Fl_Group.H"], "codegen": { "extends": "ui" }, - "skip_props": ["name", "frame.*", "xsml", "xsml:*"], - "props": { + "skip_fields": ["name", "frame.*", "xsml", "xsml:*"], + "fields": { "mixin": { "type": "string", "description": "Apply mixins to this object", - "code": "that->refresh_style(value);" + "setter": "that->refresh_style(value);" }, "label": { "type": "string", "alias": ["label.txt"], - "code": "w.copy_label(value);" + "setter": "w.copy_label(value);" }, "label.align.pos": { "type": "raw", "subtype": "fl_align_pos", - "code": "int t=field_types::fl_align_pos_i(value);if((ok=(t!=-1)))w.align((w.align()&(!FL_ALIGN_POSITION_MASK))|t);" + "setter": "int t=field_types::fl_align_pos_i(value);if((ok=(t!=-1)))w.align((w.align()&(!FL_ALIGN_POSITION_MASK))|t);" }, "label.align.img": { "type": "raw", "subtype": "fl_align_image", - "code": "int t=field_types::fl_align_image_i(value);if((ok=(t!=-1)))w.align((w.align()&(!FL_ALIGN_IMAGE_MASK))|t);" + "setter": "int t=field_types::fl_align_image_i(value);if((ok=(t!=-1)))w.align((w.align()&(!FL_ALIGN_IMAGE_MASK))|t);" }, "label.align.wrap": { "type": "raw", "subtype": "fl_align_wrap", - "code": "int t=field_types::fl_align_wrap_i(value);if((ok=(t!=-1)))w.align((w.align()&(!FL_ALIGN_WRAP))|t);" + "setter": "int t=field_types::fl_align_wrap_i(value);if((ok=(t!=-1)))w.align((w.align()&(!FL_ALIGN_WRAP))|t);" }, "label.align.clip": { "type": "raw", "subtype": "fl_align_clip", - "code": "int t=field_types::fl_align_clip_i(value);if((ok=(t!=-1)))w.align((w.align()&(!FL_ALIGN_CLIP))|t);" + "setter": "int t=field_types::fl_align_clip_i(value);if((ok=(t!=-1)))w.align((w.align()&(!FL_ALIGN_CLIP))|t);" }, "label.align.inside": { "type": "raw", "subtype": "fl_align_inside", - "code": "int t=field_types::fl_align_inside_i(value);if((ok=(t!=-1)))w.align((w.align()&(!FL_ALIGN_INSIDE))|t);" + "setter": "int t=field_types::fl_align_inside_i(value);if((ok=(t!=-1)))w.align((w.align()&(!FL_ALIGN_INSIDE))|t);" }, "label.size": { "type": "raw", "description": "A simple integer value to determine the font size, or 'default' to set the default one.", - "code": "if(strcmp(value,\"default\")==0){w.labelsize(FL_NORMAL_SIZE);}else{size_t size; if((ok=field_types::h_px(1,&size,value,that))) w.labelsize(size);}" + "setter": "if(strcmp(value,\"default\")==0){w.labelsize(FL_NORMAL_SIZE);}else{size_t size; if((ok=field_types::h_px(1,&size,value,that))) w.labelsize(size);}" }, "label.colour": { "type": "color", "alias": ["label.color"], - "code": "w.labelcolor(computed);" + "setter": "w.labelcolor(computed);" }, "label.margin": { "type": "scalar-2", "description": "Horizontal and vertical margins for the label.", - "code": "w.horizontal_label_margin(computed[0]); w.vertical_label_margin(computed[1]);" + "setter": "w.horizontal_label_margin(computed[0]); w.vertical_label_margin(computed[1]);" }, "label.margin.x": { "type": "scalar-1", "description": "Horizontal margin for the label.", - "code": "w.horizontal_label_margin(computed[0]);" + "setter": "w.horizontal_label_margin(computed[0]);" }, "label.margin.y": { "type": "scalar-1", "description": "Vertical margin for the label.", - "code": "w.vertical_label_margin(computed[0]);" + "setter": "w.vertical_label_margin(computed[0]);" }, "tooltip": { "type": "string", "alias": ["tooltip.txt"], - "code": "w.copy_tooltip(value);" + "setter": "w.copy_tooltip(value);" }, "box": { "type": "scalar-4", "description": "Origin offset (0,1) and size (2,3) for the box.", - "code": "w.position(computed[0],computed[1]);w.size(computed[2],computed[3]);" + "setter": "w.position(computed[0],computed[1]);w.size(computed[2],computed[3]);", + "getter": "/*TODO size()*/" }, "box.x": { "type": "scalar-1", - "code": "w.position(computed[0], w.y());" + "setter": "w.position(computed[0], w.y());" }, "box.y": { "type": "scalar-1", - "code": "w.position(w.x(), computed[0]);" + "setter": "w.position(w.x(), computed[0]);" }, "box.w": { "type": "scalar-1", "alias": ["box.width"], - "code": "w.size(computed[0], w.h());" + "setter": "w.size(computed[0], w.h());" }, "box.h": { "type": "scalar-1", "alias": ["box.height"], - "code": "w.size(w.w(), computed[0]);" + "setter": "w.size(w.w(), computed[0]);" }, "box.style": { "type": "raw", "subtype": "fl_boxtype", - "code": "w.box(field_types::fl_boxtype_i(value));" + "setter": "w.box(field_types::fl_boxtype_i(value));" }, "box.fixed": { "type": "flag", "description": "If this widget is child of a container like flex, set its box size as fixed", - "code": "/*TODO*/" + "setter": "/*TODO*/" }, "bg.colour": { "type": "color", "alias": ["bg.color"], "description": "Colour for both relaxed and selected states", - "code": "w.color(computed);w.selection_color(computed);" + "setter": "w.color(computed);w.selection_color(computed);" }, "bg.colour.relaxed": { "type": "color", "alias": ["bg.color.relaxed"], "description": "Colour for the relaxed state", - "code": "w.color(computed);" + "setter": "w.color(computed);" }, "bg.colour.selected": { "type": "color", "alias": ["bg.color.selected"], "description": "Colour for the selected state", - "code": "w.selection_color(computed);" + "setter": "w.selection_color(computed);" }, "active": { "type": "flag", "description": "If true set active (default).", - "code": "if(computed)w.activate();else w.deactivate();" + "setter": "if(computed)w.activate();else w.deactivate();", + "getter": "/*TODO active()*/" + }, + "active.recursive": { + "type": "flag", + "description": "Get back the actual box of the widget", + "getter": "/*TODO active_t()*/" }, "show": { "type": "flag", "description": "If true the widget is shown (default).", - "code": "if(computed)w.show();else w.hide();" + "setter": "if(computed)w.show();else w.hide();" }, "on.callback": { "type": "raw", "subtype": "$symbol", "description": "Empty string to unset.", - "code": "if(strcmp(value,\"\")==0)w.callback((Fl_Callback*)nullptr);else{auto result = that->resolve_symbol(value, true);if(result.symbol.symbol==nullptr){vs_log(severety_t::CONTINUE,that,\"Unable to find symbol `%s` for callback.\",value);}else{ui_callback_t* payload = new ui_callback_t();payload->caller=that;payload->sym=result;w.callback(ui_callback_handler,payload,true);}}" - } - }, - "computed": { - "box": { - "type": "scalar-4", - "description": "Get back the actual box of the widget", - "code": "/*TODO size()*/" + "setter": "if(strcmp(value,\"\")==0)w.callback((Fl_Callback*)nullptr);else{auto result = that->resolve_symbol(value, true);if(result.symbol.symbol==nullptr){vs_log(severety_t::CONTINUE,that,\"Unable to find symbol `%s` for callback.\",value);}else{ui_callback_t* payload = new ui_callback_t();payload->caller=that;payload->sym=result;w.callback(ui_callback_handler,payload,true);}}" }, "visible": { "type": "flag", "description": "Get back the actual box of the widget", - "code": "/*TODO visible()*/" + "getter": "/*TODO visible()*/" }, "visible.recursive": { "type": "flag", "description": "Get back the actual box of the widget", - "code": "/*TODO visible.r()*/" - }, - "active": { - "type": "flag", - "description": "Get back the actual box of the widget", - "code": "/*TODO active()*/" - }, - "active.recursive": { - "type": "flag", - "description": "Get back the actual box of the widget", - "code": "/*TODO active_t()*/" + "getter": "/*TODO visible.r()*/" } } } diff --git a/schemas/components/fl:button.json b/schemas/components/fl:button.json index 381b9668..41e0fee1 100644 --- a/schemas/components/fl:button.json +++ b/schemas/components/fl:button.json @@ -1,14 +1,10 @@ { "$schema": "../../commons/schemas/json-component.schema.json", "description": "Basic button element", - "usable": true, + "exposed": true, "type": "leaf", "headers": ["FL/Fl_Button.H"], "extends": "fl:base", "codegen": {"extends": "ui"}, - "props":{ - }, - "computed":{ - - } + "fields": {} } \ No newline at end of file diff --git a/schemas/components/fl:button.toggle.json b/schemas/components/fl:button.toggle.json index deddf81e..a76fd246 100644 --- a/schemas/components/fl:button.toggle.json +++ b/schemas/components/fl:button.toggle.json @@ -1,14 +1,10 @@ { - "$schema": "../../commons/schemas/json-component.schema.json", - "description": "Toggle button element", - "usable": true, - "type": "leaf", - "headers": ["FL/Fl_Toggle_Button.H"], - "extends": "fl:button", - "codegen": {"extends": "ui"}, - "props":{ - }, - "computed":{ - - } -} \ No newline at end of file + "$schema": "../../commons/schemas/json-component.schema.json", + "description": "Toggle button element", + "exposed": true, + "type": "leaf", + "headers": ["FL/Fl_Toggle_Button.H"], + "extends": "fl:button", + "codegen": { "extends": "ui" }, + "fields": {} +} diff --git a/schemas/components/fl:flex.json b/schemas/components/fl:flex.json index 35067c65..6f524629 100644 --- a/schemas/components/fl:flex.json +++ b/schemas/components/fl:flex.json @@ -1,50 +1,48 @@ { - "$schema": "../../commons/schemas/json-component.schema.json", - "description": "Flex container", - "usable": true, - "type": "node", - "headers": ["FL/Fl_Flex.H"], - "extends": "fl:group", - "codegen": {"extends": "ui"}, - "props":{ - "layout.type":{ - "type": "enum", - "subtype": "fl_flex_layout", - "description": "Set to `row` or `column` (default)", - "code": "/*TODO*/" - }, - "layout.gap":{ - "type":"scalar-1", - "description": "Gap between elements in this flex", - "code": "w.gap(computed[0]);" - }, - "margin":{ - "type": "scalar-4", - "description": "Margin left top right bottom", - "code": "w.margin(computed[0],computed[1],computed[2],computed[3]);" - }, - "margin.left":{ - "type": "scalar-1", - "code": "int tmp[4];w.margin(tmp,tmp+1,tmp+2,tmp+3);w.margin(computed[0],tmp[1],tmp[2],tmp[3]);" - }, - "margin.top":{ - "type": "scalar-1", - "code": "int tmp[4];w.margin(tmp,tmp+1,tmp+2,tmp+3);w.margin(tmp[0],computed[0],tmp[2],tmp[3]);" - }, - "margin.right":{ - "type": "scalar-1", - "code": "int tmp[4];w.margin(tmp,tmp+1,tmp+2,tmp+3);w.margin(tmp[0],tmp[1],computed[0],tmp[3]);" - }, - "margin.bottom":{ - "type": "scalar-1", - "code": "int tmp[4];w.margin(tmp,tmp+1,tmp+2,tmp+3);w.margin(tmp[0],tmp[1],tmp[2],computed[0]);" - }, - "layout.spacing":{ - "type":"scalar-1", - "description": "Gap between elements in this flex", - "code": "w.spacing(computed[0]);" - } + "$schema": "../../commons/schemas/json-component.schema.json", + "description": "Flex container", + "exposed": true, + "type": "node", + "headers": ["FL/Fl_Flex.H"], + "extends": "fl:group", + "codegen": { "extends": "ui" }, + "fields": { + "layout.type": { + "type": "enum", + "subtype": "fl_flex_layout", + "description": "Set to `row` or `column` (default)", + "setter": "/*TODO*/" }, - "computed":{ + "layout.gap": { + "type": "scalar-1", + "description": "Gap between elements in this flex", + "setter": "w.gap(computed[0]);" + }, + "margin": { + "type": "scalar-4", + "description": "Margin left top right bottom", + "setter": "w.margin(computed[0],computed[1],computed[2],computed[3]);" + }, + "margin.left": { + "type": "scalar-1", + "setter": "int tmp[4];w.margin(tmp,tmp+1,tmp+2,tmp+3);w.margin(computed[0],tmp[1],tmp[2],tmp[3]);" + }, + "margin.top": { + "type": "scalar-1", + "setter": "int tmp[4];w.margin(tmp,tmp+1,tmp+2,tmp+3);w.margin(tmp[0],computed[0],tmp[2],tmp[3]);" + }, + "margin.right": { + "type": "scalar-1", + "setter": "int tmp[4];w.margin(tmp,tmp+1,tmp+2,tmp+3);w.margin(tmp[0],tmp[1],computed[0],tmp[3]);" + }, + "margin.bottom": { + "type": "scalar-1", + "setter": "int tmp[4];w.margin(tmp,tmp+1,tmp+2,tmp+3);w.margin(tmp[0],tmp[1],tmp[2],computed[0]);" + }, + "layout.spacing": { + "type": "scalar-1", + "description": "Gap between elements in this flex", + "setter": "w.spacing(computed[0]);" } -} \ No newline at end of file + } +} diff --git a/schemas/components/fl:grid.json b/schemas/components/fl:grid.json index 145713e1..6be7201f 100644 --- a/schemas/components/fl:grid.json +++ b/schemas/components/fl:grid.json @@ -1,13 +1,10 @@ { - "$schema": "../../commons/schemas/json-component.schema.json", - "description": "Grid container", - "usable": true, - "type": "node", - "headers": ["FL/Fl_Grid.H"], - "extends": "fl:group", - "codegen": {"extends": "ui"}, - "props":{ - }, - "computed":{ - } -} \ No newline at end of file + "$schema": "../../commons/schemas/json-component.schema.json", + "description": "Grid container", + "exposed": true, + "type": "node", + "headers": ["FL/Fl_Grid.H"], + "extends": "fl:group", + "codegen": { "extends": "ui" }, + "fields": {} +} diff --git a/schemas/components/fl:group.json b/schemas/components/fl:group.json index 3035e947..7d6d508f 100644 --- a/schemas/components/fl:group.json +++ b/schemas/components/fl:group.json @@ -1,23 +1,21 @@ { - "$schema": "../../commons/schemas/json-component.schema.json", - "description": "Basic group element", - "usable": true, - "type": "node", - "headers": ["FL/Fl_Group.H"], - "extends": "fl:base", - "codegen": {"extends": "ui"}, - "props":{ - "box.clip":{ - "type":"flag", - "description": "Define the clipping behaviour of this group.", - "code": "w.clip_children(computed);" - }, - "resizable":{ - "type":"raw", - "description": "Set to ``, `self` or the name of a direct child", - "code": "/*TODO*/" - } + "$schema": "../../commons/schemas/json-component.schema.json", + "description": "Basic group element", + "exposed": true, + "type": "node", + "headers": ["FL/Fl_Group.H"], + "extends": "fl:base", + "codegen": { "extends": "ui" }, + "fields": { + "box.clip": { + "type": "flag", + "description": "Define the clipping behaviour of this group.", + "setter": "w.clip_children(computed);" }, - "computed":{ + "resizable": { + "type": "raw", + "description": "Set to ``, `self` or the name of a direct child", + "setter": "/*TODO*/" } -} \ No newline at end of file + } +} diff --git a/schemas/components/fl:input.json b/schemas/components/fl:input.json index e14beb2a..741142e8 100644 --- a/schemas/components/fl:input.json +++ b/schemas/components/fl:input.json @@ -1,13 +1,10 @@ { - "$schema": "../../commons/schemas/json-component.schema.json", - "description": "Basic input element", - "usable": true, - "type": "leaf", - "headers": ["FL/Fl_Input.H"], - "extends": "fl:base", - "codegen": {"extends": "ui"}, - "props":{ - }, - "computed":{ - } -} \ No newline at end of file + "$schema": "../../commons/schemas/json-component.schema.json", + "description": "Basic input element", + "exposed": true, + "type": "leaf", + "headers": ["FL/Fl_Input.H"], + "extends": "fl:base", + "codegen": { "extends": "ui" }, + "fields": {} +} diff --git a/schemas/components/fl:label.json b/schemas/components/fl:label.json index 1273a160..fee87ad6 100644 --- a/schemas/components/fl:label.json +++ b/schemas/components/fl:label.json @@ -1,14 +1,10 @@ { - "$schema": "../../commons/schemas/json-component.schema.json", - "description": "Label element to a window", - "usable": true, - "type": "leaf", - "headers": ["FL/Fl_Box.H"], - "extends": "fl:base", - "codegen": {"extends": "ui"}, - "props":{ - }, - "computed":{ - - } -} \ No newline at end of file + "$schema": "../../commons/schemas/json-component.schema.json", + "description": "Label element to a window", + "exposed": true, + "type": "leaf", + "headers": ["FL/Fl_Box.H"], + "extends": "fl:base", + "codegen": { "extends": "ui" }, + "fields": {} +} diff --git a/schemas/components/fl:pack.json b/schemas/components/fl:pack.json index e03ae137..93069d61 100644 --- a/schemas/components/fl:pack.json +++ b/schemas/components/fl:pack.json @@ -1,13 +1,10 @@ { - "$schema": "../../commons/schemas/json-component.schema.json", - "description": "Pack container", - "usable": true, - "type": "node", - "headers": ["FL/Fl_Pack.H"], - "extends": "fl:group", - "codegen": {"extends": "ui"}, - "props":{ - }, - "computed":{ - } -} \ No newline at end of file + "$schema": "../../commons/schemas/json-component.schema.json", + "description": "Pack container", + "exposed": true, + "type": "node", + "headers": ["FL/Fl_Pack.H"], + "extends": "fl:group", + "codegen": { "extends": "ui" }, + "fields": {} +} diff --git a/schemas/components/fl:scroll.json b/schemas/components/fl:scroll.json index 538cc206..79dfbe5e 100644 --- a/schemas/components/fl:scroll.json +++ b/schemas/components/fl:scroll.json @@ -1,13 +1,10 @@ { - "$schema": "../../commons/schemas/json-component.schema.json", - "description": "Scroll container", - "usable": true, - "type": "node", - "headers": ["FL/Fl_Scroll.H"], - "extends": "fl:group", - "codegen": {"extends": "ui"}, - "props":{ - }, - "computed":{ - } -} \ No newline at end of file + "$schema": "../../commons/schemas/json-component.schema.json", + "description": "Scroll container", + "exposed": true, + "type": "node", + "headers": ["FL/Fl_Scroll.H"], + "extends": "fl:group", + "codegen": { "extends": "ui" }, + "fields": {} +} diff --git a/schemas/components/fl:textbox.json b/schemas/components/fl:textbox.json index cdb96b89..7bc56d29 100644 --- a/schemas/components/fl:textbox.json +++ b/schemas/components/fl:textbox.json @@ -2,22 +2,17 @@ "$schema": "../../commons/schemas/json-component.schema.json", "description": "Blabla bla", "type": "leaf", + "exposed": true, "headers": ["FL/Fl_Button.H"], "codegen": { "extends": "ui" }, "extends": "fl:base", - "props": { + "fields": { "somethig": { "type": "string", - "code": "", - "alias": ["alias-a", "alias-b"] - } - }, - "computed": { - "computed-a": { - "type": "string", - "code": "", + "setter": "", + "getter": "", "alias": ["alias-a", "alias-b"] } } diff --git a/schemas/components/fl:window.json b/schemas/components/fl:window.json index 5aad333f..d122a3f9 100644 --- a/schemas/components/fl:window.json +++ b/schemas/components/fl:window.json @@ -1,22 +1,21 @@ { "$schema": "../../commons/schemas/json-component.schema.json", "description": "Base element to a window", - "usable": true, + "exposed": true, "type": "node", "headers": ["FL/Fl_Window.H", "FL/Fl_SVG_Image.H"], "extends": "fl:base", "codegen": { "extends": "ui" }, - "props": { + "fields": { "border": { "type": "flag", "description": "True if using a border around", - "code": "w.border(computed);" + "setter": "w.border(computed);" }, "icon": { "type": "string", "description": "True if using a border around", - "code": "w.icon(new Fl_SVG_Image(value));" + "setter": "w.icon(new Fl_SVG_Image(value));" } - }, - "computed": {} + } } diff --git a/scripts/codegen/components-new.schema.ts b/scripts/codegen/components-new.schema.ts deleted file mode 100644 index 6b17e255..00000000 --- a/scripts/codegen/components-new.schema.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { Type as t } from "@sinclair/typebox" - -//New schema to replace the old one. -//Now props, computed, setters and getters are better linked together - -export const type_schema = t.Union([ - t.Literal('flag'), - t.Literal('enum'), - t.Literal('raw'), - t.Literal('path'), - t.Literal('string'), - t.Literal('color'), - t.Literal('scalar-1'), - t.Literal('scalar-2'), - t.Literal('scalar-4') -], { description: 'type', default: 'string' }); - -const entries_schema = t.Record(t.String(), t.Object({ - type: type_schema, - subtype: t.Optional(t.String()), - parse: t.Union([t.String(), t.Null()], { default: null, description: "From string to object. Default based on `type` (and `subtype`)" }), - //`serialize` would be the dual of parse, but it is not needed as types are all defined and serialization methods will not be custom. - setter: t.Union([t.String(), t.Null()], { default: null, description: "Operations on setting. If not set, this field is only available as a computed value." }), - getter: t.Union([t.String(), t.Null()], { default: null, description: "Implementation on how to extract value from object. If not set the field is not observable." }), - description: t.Optional(t.String()), - semantic: t.Optional(t.Boolean({ default: false, description: 'If true, this field has a strong semantic meaning. Used for semantic serialization of a document.' })), - alias: t.Optional(t.Array(t.String(), { description: "alias names", default: [] })) -})) - -export const widget_schema = t.Object({ - $schema: t.Optional(t.String()), - ns: t.Optional(t.String({ description: "Namespace for this component, for example `fl` in case of fltk wrappers." })), - name: t.Optional(t.String()), - exposed: t.Optional(t.Boolean({ default: true, description: "If true, this widget will be usable from XML and shown in the documentation, otherwise it is not exported." })), - description: t.Optional(t.String({ description: "While optional, this field is extremely important for exposed widgets" })), - use_main_header: t.Union([t.Null(), t.String()], { default: null, description: "If set, no class definition will be generated. It will instread load one already present. Used to track and document widgets which are not fully autogenerated." }), - headers: t.Optional(t.Array(t.String(), { description: "List of header files used by the widget in its public header file." })), - private_headers: t.Optional(t.Array(t.String(), { description: "List of header files used by the widget in its generated cpp file." })), - type: t.Union([t.Literal('leaf'), t.Literal('node'), t.Literal('container'), t.Literal('slot'), t.Literal('slot-contaiener')], { description: "Type of frame an instance of this widget class will default top." }), - codegen: t.Object({ - extends: t.Union([t.Null(), t.String()], { default: null, description: "If set, a class to inherit from, usually related to the fltk widget." }), - set_tail: t.Optional(t.Union([t.Null(), t.String()], { description: "If specified, what to do if a getter is not matched." })), - get_tail: t.Optional(t.Union([t.Null(), t.String()], { description: "If specified, what to do if a setter is not matched." })), - }, { additionalProperties: false }), - extends: t.Union([t.Null(), t.String()], { default: null, description: "If set, a vs widget to inherit from (not a c++ class name)" }), - skip_fields: t.Optional(t.Array(t.String(), { default: [], description: "Properties to be matched when set, but ignored." })), - fields: entries_schema, -}, { additionalProperties: false }) \ No newline at end of file diff --git a/scripts/codegen/components.schema.ts b/scripts/codegen/components.schema.ts index 9771fb56..b696cf7c 100644 --- a/scripts/codegen/components.schema.ts +++ b/scripts/codegen/components.schema.ts @@ -1,32 +1,48 @@ import { Type as t } from "@sinclair/typebox" -export const type_schema = t.Union([t.Literal('flag'), t.Literal('enum'), t.Literal('raw'), t.Literal('string'), t.Literal('path'), t.Literal('color'), t.Literal('scalar-1'), t.Literal('scalar-2'), t.Literal('scalar-4')], { description: 'type', default: 'string' }); +//New schema to replace the old one. +//Now props, computed, setters and getters are better linked together + +export const type_schema = t.Union([ + t.Literal('flag'), + t.Literal('enum'), + t.Literal('raw'), + t.Literal('path'), + t.Literal('string'), + t.Literal('color'), + t.Literal('scalar-1'), + t.Literal('scalar-2'), + t.Literal('scalar-4') +], { description: 'type', default: 'string' }); const entries_schema = t.Record(t.String(), t.Object({ type: type_schema, subtype: t.Optional(t.String()), - code: t.Union([t.String(), t.Null()], { default: null }), + parse: t.Optional(t.Union([t.String(), t.Null()], { default: undefined, description: "From string to object. Default based on `type` (and `subtype`)" })), + //`serialize` would be the dual of parse, but it is not needed as types are all defined and serialization methods will not be custom. + setter: t.Optional(t.Union([t.String(), t.Null()], { default: undefined, description: "Operations on setting. If undefined, this field is only available as a computed value. If null use default" })), + getter: t.Optional(t.Union([t.String(), t.Null()], { default: undefined, description: "Implementation on how to extract value from object. If undefined the field is not observable. If null use default" })), description: t.Optional(t.String()), + semantic: t.Optional(t.Boolean({ default: false, description: 'If true, this field has a strong semantic meaning. Used for semantic serialization of a document.' })), alias: t.Optional(t.Array(t.String(), { description: "alias names", default: [] })) -})) +}, { additionalProperties: false })) export const widget_schema = t.Object({ $schema: t.Optional(t.String()), - ns: t.Optional(t.String()), + ns: t.Optional(t.String({ description: "Namespace for this component, for example `fl` in case of fltk wrappers." })), name: t.Optional(t.String()), - usable: t.Optional(t.Boolean({ default: true })), - description: t.Optional(t.String()), - use_main_header: t.Union([t.Null(), t.String()], { default: null }), - headers: t.Optional(t.Array(t.String())), - type: t.Union([t.Literal('leaf'), t.Literal('node'), t.Literal('container'), t.Literal('slot'), t.Literal('slot-contaiener')]), - //public: t.Optional(t.Boolean({ default: true })), //probably replaced by usable + exposed: t.Optional(t.Boolean({ default: true, description: "If true, this widget will be usable from XML and shown in the documentation, otherwise it is not exported." })), + description: t.Optional(t.String({ description: "While optional, this field is extremely important for exposed widgets" })), + use_main_header: t.Union([t.Null(), t.String()], { default: null, description: "If set, no class definition will be generated. It will instread load one already present. Used to track and document widgets which are not fully autogenerated." }), + headers: t.Optional(t.Array(t.String(), { description: "List of header files used by the widget in its public header file." })), + private_headers: t.Optional(t.Array(t.String(), { description: "List of header files used by the widget in its generated cpp file." })), + type: t.Union([t.Literal('leaf'), t.Literal('node'), t.Literal('container'), t.Literal('slot'), t.Literal('slot-contaiener')], { description: "Type of frame an instance of this widget class will default top." }), codegen: t.Object({ - extends: t.Union([t.Null(), t.String()], { default: null }), - props_tail: t.Optional(t.Union([t.Null(), t.String()])), - computed_tail: t.Optional(t.Union([t.Null(), t.String()])), + extends: t.Union([t.Null(), t.String()], { default: null, description: "If set, a class to inherit from, usually related to the fltk widget." }), + set_tail: t.Optional(t.Union([t.Null(), t.String()], { description: "If specified, what to do if a getter is not matched." })), + get_tail: t.Optional(t.Union([t.Null(), t.String()], { description: "If specified, what to do if a setter is not matched." })), }, { additionalProperties: false }), - extends: t.Union([t.Null(), t.String()], { default: null }), - skip_props: t.Optional(t.Array(t.String(), { default: [], description: "Properties to be matched but ignored" })), - props: entries_schema, - computed: entries_schema, + extends: t.Union([t.Null(), t.String()], { default: null, description: "If set, a vs widget to inherit from (not a c++ class name)" }), + skip_fields: t.Optional(t.Array(t.String(), { default: [], description: "Properties to be matched when set, but ignored." })), + fields: entries_schema, }, { additionalProperties: false }) \ No newline at end of file diff --git a/scripts/codegen/gen-components-new.tsx b/scripts/codegen/gen-components-new.tsx deleted file mode 100644 index 822da4cf..00000000 --- a/scripts/codegen/gen-components-new.tsx +++ /dev/null @@ -1,214 +0,0 @@ -/* - Replacement for the old autogen to use the simplified props/computed getter/setter model. - TODO: Implement - - Code generator for code and derived specs from json schemas. - Use `quick` as optional arg to avoid generating the meson file again. - This will speed up compilation considerably and is fine as long as you have not added or removed files in the schema folder. -*/ - -import type { Static } from '@sinclair/typebox'; -import { $ } from 'bun' -import { Glob } from "bun"; -import { parse } from "node:path" -import { widget_schema, type_schema } from './components-new.schema'; -import { Value } from '@sinclair/typebox/value'; - -import { render, JSXXML } from 'jsx-xml' - - -function make_type_code(type: Static, subtype: string, code: string) { - if (type === 'raw') return code; - if (type === 'flag') return `bool computed; if((ok=ui_tree::h_flag(&computed,value,that))){${code}}` - else if (type === 'color') return `uint32_t computed; if((ok=ui_tree::h_colour(&computed,value,that))){${code}}` - else if (type === 'string') return code; - else if (type === 'scalar-1') return `size_t computed[1]; if((ok = ui_tree::h_px(1,computed,value,that))){${code}}` - else if (type === 'scalar-2') return `size_t computed[2]; if((ok = ui_tree::h_px(2,computed,value,that))){${code}}` - else if (type === 'scalar-4') return `size_t computed[4]; if((ok = ui_tree::h_px(4,computed,value,that))){${code}}` - else if (type === 'enum') return `int computed = ui_tree::${subtype}_i(value);if((ok=(computed!=-1))){${code}}` -} - -function gen_cpp(data: Static) { - const cextends = data.extends==null?null:data.extends.split(':'); - const cppname = `${data.ns}_${data.name?.replaceAll('.','_')}`; - - let class_decl = data.use_main_header === null ? ` -#pragma once - -#include -#include -${cextends ? `#include \n`:``} -${data.headers ? data.headers.map(x => `#include <${x}>\n`) : ``} - -namespace vs{ - -class ${cppname} : public ${data.codegen.extends}{ - public: - template - ${cppname}(ui_base* p,Args ...w):${data.codegen.extends}(p,w...){} - virtual frame_type_t default_frame_type() override {return frame_type_t::LEAF;} - virtual const char* class_name() override{return "${data.ns}:${data.name}";} - - virtual ~${cppname}(){} - - virtual int apply_prop(const char* prop, const char* value) override {return ${cppname}::_apply_prop(this,prop,value);} - virtual int get_computed(const char* prop, const char ** value) override {return ${cppname}::_get_computed(this,prop,value);}; - - public: - static int _apply_prop(${cppname}* that, const char* prop, const char* value); - static int _get_computed(${cppname}* that, const char* prop, const char** value); -}; - -} - `: - `#pragma once -#include <${data.use_main_header}> -`; - - let class_impl = ` -#include - -namespace vs{ - -int ${cppname}::_apply_prop(${cppname}* that, const char* prop, const char* value){ - auto& w = that->widget(); - bool ok = true; - if(false){} - ${(data.skip_props??[]).map(x=>{ - if(x[x.length-1]=='*')return `else if (strncmp(prop, "${x}", ${x.length-1})==0){}` - else return `else if(strcmp(prop,"${x}")==0){}` - }).join('\n\t')} - ${Object.entries(data.props).map(x => { - x[1].alias=x[1].alias??[] - x[1].alias.push(x[0]); - return x[1].alias.map(y => ` - ${x[1].description ? `//${x[1].description}` : ``} - else if(strcmp(prop,"${y}")==0){ - ${make_type_code(x[1].type, x[1].subtype ?? "", x[1].code??"")} - }`).join('\n') - } - ).join('\n') - } - ${cextends === null ? `else {return 2;}\nreturn ok?0:1;` : `if(!ok) return 1; else return ${data.codegen.props_tail ?? `${cextends[0]}_${cextends[1]}`}::_apply_prop((${data.codegen.props_tail ?? `${cextends[0]}_${cextends[1]}`}*)that,prop,value);`} -} - -int ${cppname}:: _get_computed(${cppname} * that, const char* prop, const char** value) { - auto& w = that->widget(); - bool ok = true; - if(false){} - ${Object.entries(data.computed).map(x => { - x[1].alias?.push(x[0]); - return x[1].alias?.map(y => ` - ${x[1].description ? `//${x[1].description}` : ``} - else if(strcmp(prop,"${y}")==0){ - ${x[1].code} - }`).join('\n') - } - ).join('\n') - } - ${cextends === null ? `else {return 2;}\nreturn ok?0:1;` : `if(!ok) return 1; else return ${data.codegen.props_tail ?? `${cextends[0]}_${cextends[1]}`}::_get_computed((${data.codegen.props_tail ?? `${cextends[0]}_${cextends[1]}`}*)that,prop,value);`} -} - -} -`; - - let parser_selector = - data.usable!=false ? - data.type === 'leaf' ? `mkNSLeafWidget(${data.ns}, ${data.name}, ${cppname})` : - data.type === 'node' ? `mkNSNodeWidget(${data.ns}, ${data.name}, ${cppname}) ` : - data.type === 'container' ? `mkNSContainerWidget(${data.ns}, ${data.name}, ${cppname})` : - data.type === 'slot' ? `mkNSSlotWidget(${data.ns}, ${data.name}, ${cppname})` : - data.type === 'slot-contaiener' ? `mkNSSlotContainerWidget(${data.ns}, ${data.name}, ${cppname})` : - ``:``; - return [class_decl, class_impl, parser_selector] -} - - -function gen_d_ts(data: Static) { } - -function gen_xsd(data: Static) { } - -function gen_xml_editor(data: Static) { - return - {data.description ?? "No description provided"} - {Object.entries(data.props).map(x => {x[1].description ?? 'No description provided'})} - {Object.entries(data.computed).map(x => {x[1].description ?? 'No description provided'})} - -} - -if (process.argv[2] != 'quick') { - await $`rm -rf ./src/components/autogen/` -} -await $`mkdir -p ./src/components/autogen/` - -await $`rm -rf ./include/components/autogen/` -await $`mkdir -p ./include/components/autogen/` - -await $`rm -rf ./commons/schemas/components/` -await $`mkdir -p ./commons/schemas/components/` - -const glob = new Glob("./schemas/components/**/*.json"); - - -const cpp_files = [] -const h_files = [] -const parser_entries = [] - -for await (const file of glob.scan(".")) { - const tmp = Value.Default(widget_schema, (await Bun.file(file).json())) - if (!Value.Check(widget_schema, tmp)) { - console.warn(`[SKIP] ${file} since its syntax is not comformant`); - for (const i of [...Value.Errors(widget_schema, tmp)]) { - console.error(i.path, i.message); - } - } - else { - const namepieces = parse(file).name.split(':'); - tmp.ns ??= namepieces[0] - tmp.name ??= namepieces[1] - - //Cpp code - cpp_files.push(`./src/components/autogen/${tmp.ns}/${tmp.name}.cpp`) - h_files.push(`components/autogen/${tmp.ns}/${tmp.name}.hpp`) - { - const ret = gen_cpp(tmp); - - await $`mkdir -p ./include/components/autogen/${tmp.ns}/` - await Bun.write(`./include/components/autogen/${tmp.ns}/${tmp.name}.hpp`, ret[0]) - - await $`mkdir -p ./src/components/autogen/${tmp.ns}/` - await Bun.write(`./src/components/autogen/${tmp.ns}/${tmp.name}.cpp`, ret[1]) - parser_entries.push(ret[2]) - } - - //XML Model for the Editor - { - const ret = gen_xml_editor(tmp); - await $`mkdir -p ./commons/schemas/components/${tmp.ns}/` - await Bun.write(`./commons/schemas/components/${tmp.ns}/${tmp.name}.xml`, render(ret)) - } - - //XML Schema for aid when writing code - //TODO: - - //Typescript types - //TODO: - - console.log(`[DONE] ${parse(file).base}`) - } -} - -await Bun.write('./include/components/autogen/index.hpp', h_files.map(x => `#include <${x}>`).join('\n')) -await Bun.write('./src/ui.xml-widgets.autogen.cpp', parser_entries.join('\n')) - -//TODO -await Bun.write('./include/cbindings/components.autogen.h', '') -//TODO -await Bun.write('./src/cbindings/components.autogen.cpp', '') - -//Save the schema, so that our json files can all be validated in the editor while writing them. -await Bun.write('./commons/schemas/json-component.schema.json', JSON.stringify(widget_schema, null, 4)) - -if (process.argv[2] != 'quick') { - await Bun.write('./src/components/autogen/meson.build', `autogen_components = [${cpp_files.map(x => `'${x}'`)}]`) -} \ No newline at end of file diff --git a/scripts/codegen/gen-components.tsx b/scripts/codegen/gen-components.tsx index 0e07c0c8..15c45952 100644 --- a/scripts/codegen/gen-components.tsx +++ b/scripts/codegen/gen-components.tsx @@ -15,15 +15,16 @@ import { render, JSXXML } from 'jsx-xml' function make_type_code(type: Static, subtype: string, code: string) { - if (type === 'raw') return code; - if (type === 'flag') return `bool computed; if((ok=field_types::h_flag(&computed,value,that))){${code}}` + if (false) { } + else if (type === 'flag') return `bool computed; if((ok=field_types::h_flag(&computed,value,that))){${code}}` + else if (type === 'enum') return `int computed = field_types::${subtype}_i(value);if((ok=(computed!=-1))){${code}}` + else if (type === 'raw') return code; + else if (type === 'path') return `/*TODO*/` else if (type === 'color') return `uint32_t computed; if((ok=field_types::h_colour(&computed,value,that))){${code}}` else if (type === 'string') return code; else if (type === 'scalar-1') return `size_t computed[1]; if((ok = field_types::h_px(1,computed,value,that))){${code}}` else if (type === 'scalar-2') return `size_t computed[2]; if((ok = field_types::h_px(2,computed,value,that))){${code}}` else if (type === 'scalar-4') return `size_t computed[4]; if((ok = field_types::h_px(4,computed,value,that))){${code}}` - else if (type === 'enum') return `int computed = field_types::${subtype}_i(value);if((ok=(computed!=-1))){${code}}` - else if (type === 'path') return `/*TODO*/` } function gen_cpp(data: Static) { @@ -73,46 +74,46 @@ int ${cppname}::_apply_prop(${cppname}* that, const char* prop, const char* valu auto& w = that->widget(); bool ok = true; if(false){} - ${(data.skip_props ?? []).map(x => { + ${(data.skip_fields ?? []).map(x => { if (x[x.length - 1] == '*') return `else if (strncmp(prop, "${x}", ${x.length - 1})==0){}` else return `else if(strcmp(prop,"${x}")==0){}` }).join('\n\t')} - ${Object.entries(data.props).map(x => { + ${Object.entries(data.fields).filter(x => x[1].setter != undefined).map(x => { x[1].alias = x[1].alias ?? [] x[1].alias.push(x[0]); return x[1].alias.map(y => ` ${x[1].description ? `//${x[1].description}` : ``} else if(strcmp(prop,"${y}")==0){ - ${make_type_code(x[1].type, x[1].subtype ?? "", x[1].code ?? "")} + ${make_type_code(x[1].type, x[1].subtype ?? "", x[1].setter ?? "")} }`).join('\n') } ).join('\n') } - ${cextends === null ? `else {return 2;}\nreturn ok?0:1;` : `if(!ok) return 1; else return ${data.codegen.props_tail ?? `${cextends[0]}_${cextends[1]}`}::_apply_prop((${data.codegen.props_tail ?? `${cextends[0]}_${cextends[1]}`}*)that,prop,value);`} + ${cextends === null ? `else {return 2;}\nreturn ok?0:1;` : `if(!ok) return 1; else return ${data.codegen.set_tail ?? `${cextends[0]}_${cextends[1]}`}::_apply_prop((${data.codegen.set_tail ?? `${cextends[0]}_${cextends[1]}`}*)that,prop,value);`} } int ${cppname}:: _get_computed(${cppname} * that, const char* prop, const char** value) { auto& w = that->widget(); bool ok = true; if(false){} - ${Object.entries(data.computed).map(x => { + ${Object.entries(data.fields).filter(x => x[1].getter != undefined).map(x => { x[1].alias?.push(x[0]); return x[1].alias?.map(y => ` ${x[1].description ? `//${x[1].description}` : ``} else if(strcmp(prop,"${y}")==0){ - ${x[1].code} + ${x[1].getter} }`).join('\n') } ).join('\n') } - ${cextends === null ? `else {return 2;}\nreturn ok?0:1;` : `if(!ok) return 1; else return ${data.codegen.props_tail ?? `${cextends[0]}_${cextends[1]}`}::_get_computed((${data.codegen.props_tail ?? `${cextends[0]}_${cextends[1]}`}*)that,prop,value);`} + ${cextends === null ? `else {return 2;}\nreturn ok?0:1;` : `if(!ok) return 1; else return ${data.codegen.get_tail ?? `${cextends[0]}_${cextends[1]}`}::_get_computed((${data.codegen.get_tail ?? `${cextends[0]}_${cextends[1]}`}*)that,prop,value);`} } } `; let parser_selector = - data.usable != false ? + data.exposed != false ? data.type === 'leaf' ? `mkNSLeafWidget(${data.ns}, ${data.name}, ${cppname})` : data.type === 'node' ? `mkNSNodeWidget(${data.ns}, ${data.name}, ${cppname}) ` : data.type === 'container' ? `mkNSContainerWidget(${data.ns}, ${data.name}, ${cppname})` : @@ -130,8 +131,14 @@ function gen_xsd(data: Static) { } function gen_xml_editor(data: Static) { return {data.description ?? "No description provided"} - {Object.entries(data.props).map(x => {x[1].description ?? 'No description provided'})} - {Object.entries(data.computed).map(x => {x[1].description ?? 'No description provided'})} + {Object.entries(data.fields).map(x => + {x[1].description ?? 'No description provided'})} + } @@ -146,6 +153,9 @@ await $`mkdir -p ./include/components/autogen/` await $`rm -rf ./commons/schemas/components/` await $`mkdir -p ./commons/schemas/components/` +//Save the schema, so that our json files can all be validated in the editor while writing them. +await Bun.write('./commons/schemas/json-component.schema.json', JSON.stringify(widget_schema, null, 4)) + const glob = new Glob("./schemas/components/**/*.json"); @@ -205,9 +215,6 @@ await Bun.write('./include/cbindings/components.autogen.h', '') //TODO await Bun.write('./src/cbindings/components.autogen.cpp', '') -//Save the schema, so that our json files can all be validated in the editor while writing them. -await Bun.write('./commons/schemas/json-component.schema.json', JSON.stringify(widget_schema, null, 4)) - if (process.argv[2] != 'quick') { await Bun.write('./src/components/autogen/meson.build', `autogen_components = [${cpp_files.map(x => `'${x}'`)}]`) } diff --git a/src/app/main.cpp b/src/app/main.cpp index 4cc4c9ac..e262e5d8 100755 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -20,42 +20,44 @@ #include #include +vs::global_ctx_t globals; + using namespace vs; int run(const char* path, const char *entry, const char* profile, const char* tests=nullptr){ - globals::env.computed_policies.debug(); //To set an initial record in the debug file. - globals::env.computed_policies.inherit(policies_t::from_env()); - globals::path_env = mk_env(path, entry); + globals.env.computed_policies.debug(); //To set an initial record in the debug file. + globals.env.computed_policies.inherit(policies_t::from_env()); + globals.path_env = mk_env(globals, path, entry); std::cout<<"\n--------- paths ---------\n"; - std::cout <<"cwd: "< fetcher(resolve_path& base, resolve_path::from_t from,const char* src, bool promote, bool preserve){ +std::tuple fetcher(global_ctx_t& globals, resolve_path& base, resolve_path::from_t from,const char* src, bool promote, bool preserve){ auto ret = base(from,src); if(ret.first==resolve_path::reason_t::OK){ //If present, use the one cached. { - auto found = globals::mem_storage.get({ret.second.location,0,cache::resource_t::BUFFER}); + auto found = globals.mem_storage.get({ret.second.location,0,cache::resource_t::BUFFER}); if(found!=nullptr){ cache::buffer_t bf = * (cache::buffer_t*)found->ref.get(); return {resolve_path::reason_t::OK,bf,ret.second}; @@ -57,8 +57,8 @@ std::tuple fetcher(re } if(ret.second.type==rpath_type_t::FS){ - auto res = globals::mem_storage.fetch_from_fs({ret.second.location,0,cache::resource_t::BUFFER,promote,preserve}); - if(res==globals::mem_storage.end()){ + auto res = globals.mem_storage.fetch_from_fs({ret.second.location,0,cache::resource_t::BUFFER,promote,preserve}); + if(res==globals.mem_storage.end()){ return {resolve_path::reason_t::NOT_FOUND,{nullptr,0},ret.second}; } else{ @@ -68,8 +68,8 @@ std::tuple fetcher(re } # ifdef HAS_CURL else if(ret.second.type==rpath_type_t::HTTP){ - auto res = globals::mem_storage.fetch_from_http({ret.second.as_string(),0,cache::resource_t::BUFFER,promote,preserve}); - if(res==globals::mem_storage.end()){ + auto res = globals.mem_storage.fetch_from_http({ret.second.as_string(),0,cache::resource_t::BUFFER,promote,preserve}); + if(res==globals.mem_storage.end()){ return {resolve_path::reason_t::NOT_FOUND,{nullptr,0},ret.second}; } else{ @@ -78,8 +78,8 @@ std::tuple fetcher(re } } else if(ret.second.type==rpath_type_t::HTTPS){ - auto res = globals::mem_storage.fetch_from_https({ret.second.as_string(),0,cache::resource_t::BUFFER,promote,preserve}); - if(res==globals::mem_storage.end()){ + auto res = globals.mem_storage.fetch_from_https({ret.second.as_string(),0,cache::resource_t::BUFFER,promote,preserve}); + if(res==globals.mem_storage.end()){ return {resolve_path::reason_t::NOT_FOUND,{nullptr,0},ret.second}; } else{ diff --git a/src/globals.cpp b/src/globals.cpp index 14bd63a4..4023b2ac 100644 --- a/src/globals.cpp +++ b/src/globals.cpp @@ -13,20 +13,9 @@ js_rt_t js_rt; cache::mem_storage_t mem_storage; vs_test_debug_t debug; -vs_test_debug_t::vs_test_debug_t(){auto file=getenv("VS_DEBUG_FILE");if(file!=nullptr)fd=fopen(file,"w+");} -vs_test_debug_t::~vs_test_debug_t(){if(fd!=nullptr)fclose(fd);} - -void vs_test_debug_t::operator()(const char* field, const char* value){ - if(fd==nullptr)return; - else{ - auto now = std::chrono::system_clock::now(); - fprintf(fd,"%s\t%s\t%ld\n",field,value,std::chrono::duration_cast(now.time_since_epoch()).count()); - } -} //TODO: Fill in with the basic types supported. -field_models_t value_models; - +//field_models_t value_models; } } diff --git a/src/loader.cpp b/src/loader.cpp index d802c25c..badb7a99 100644 --- a/src/loader.cpp +++ b/src/loader.cpp @@ -8,11 +8,12 @@ #include #include #include -#include +//TODO:Remove namespace vs { -app_loader::app_loader(const char *profile, const char *path) { +app_loader::app_loader(global_ctx_t& globals, const char *profile, const char *path) { + #ifdef HAS_CURL curl_global_init(CURL_GLOBAL_ALL); #endif @@ -22,8 +23,8 @@ app_loader::app_loader(const char *profile, const char *path) { std::string profile_path = profile != nullptr - ? (globals::path_env.userdata_path.as_string() + profile + ".xml") - : (globals::path_env.userdata_path.as_string() + "default.xml"); + ? (globals.path_env.userdata_path.as_string() + profile + ".xml") + : (globals.path_env.userdata_path.as_string() + "default.xml"); pugi::xml_parse_result result = doc.load_file(profile_path.c_str()); if (!result) { std::cout << "XML [" << profile_path @@ -36,7 +37,7 @@ app_loader::app_loader(const char *profile, const char *path) { doc.load_string(embedded_profile); } - root = new ui_tree_xml(); + root = new ui_tree_xml(globals,nullptr,nullptr,nullptr); if (root->load(path, ui_tree::type_t::APP) != 0) { throw "Unable to process file"; } else { @@ -51,9 +52,9 @@ int app_loader::test() { } int app_loader::run() { - globals::mem_storage.cleanup(); + root->globals.mem_storage.cleanup(); root->cleanup(); - if (!globals::env.computed_policies.headless) { + if (!root->globals.env.computed_policies.headless) { auto t = Fl::run(); delete root; root = nullptr; diff --git a/src/pipelines/quickjs-js.cpp b/src/pipelines/quickjs-js.cpp index 1f3bb19a..81f894ab 100644 --- a/src/pipelines/quickjs-js.cpp +++ b/src/pipelines/quickjs-js.cpp @@ -1,3 +1,5 @@ +#if VS_USE_QJS + #include #include @@ -217,9 +219,9 @@ std::shared_ptr> qjs_js_pipeline_apply(const std::shared_ptr qjs_js_pipeline(bool is_runtime, vs::ui_base* obj, const char* src, void* nctx, void(*error_fn)(void*,const char*), const char *link_with){ +std::shared_ptr qjs_js_pipeline(global_ctx_t& globals, bool is_runtime, vs::ui_base* obj, const char* src, void* nctx, void(*error_fn)(void*,const char*), const char *link_with){ //std::shared_ptr _ctx = std::make_shared((JSRuntime*)(globals::js_rt())); - std::shared_ptr _ctx = std::shared_ptr( new quickjs_t((JSRuntime*)(globals::js_rt())), +[](void* o){delete (quickjs_t*)o;}); + std::shared_ptr _ctx = std::shared_ptr( new quickjs_t((JSRuntime*)(globals.js_rt())), +[](void* o){delete (quickjs_t*)o;}); auto& ctx = *_ctx; @@ -328,4 +330,6 @@ std::shared_ptr qjs_js_pipeline(bool is_runtime, vs::ui_base* obj, co } -} \ No newline at end of file +} + +#endif \ No newline at end of file diff --git a/src/pipelines/tcc-c.cpp b/src/pipelines/tcc-c.cpp index 90c3a8f6..6fda7a26 100644 --- a/src/pipelines/tcc-c.cpp +++ b/src/pipelines/tcc-c.cpp @@ -1,3 +1,5 @@ +#if VS_USE_TCC + #include "globals.hpp" #include "ui-frame.hpp" #include "ui.hpp" @@ -22,14 +24,40 @@ void tcc_log_symbol_func_xml(const pugi::xml_node& ctx, const char * msg, const ui_tree_xml::log(severety_t::INFO, ctx , msg, name); } +char* itoa(int value, char* result, int base) { + // check that the base if valid + if (base < 2 || base > 36) { *result = '\0'; return result; } + + char* ptr = result, *ptr1 = result, tmp_char; + int tmp_value; + + do { + tmp_value = value; + value /= base; + *ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz" [35 + (tmp_value - value * base)]; + } while ( value ); + + // Apply negative sign + if (tmp_value < 0) *ptr++ = '-'; + *ptr-- = '\0'; + + // Reverse the string + while(ptr1 < ptr) { + tmp_char = *ptr; + *ptr--= *ptr1; + *ptr1++ = tmp_char; + } + return result; +} + //'/home/checkroom/Documents/projects/vs-fltk/subprojects/libtcc/tcc' test.c -I../../subprojects/libtcc/include/ -L. -L../../subprojects/libtcc -lapp -static void vs_debug(const char* k, const char* v){globals::debug(k,v);} +static void vs_debug(global_ctx_t& global, const char* k, const char* v){/*globals.debug(k,v);*/} #define LIB(x) script->add_sym(#x, (void*) x) #define LIBT(x,t) script->add_sym(#x, (void*) t x) -std::shared_ptr tcc_c_pipeline(bool is_runtime, vs::ui_base* obj, const char* src, void* ctx, void(*error_fn)(void*,const char*), bool compact, const char *link_with){ +std::shared_ptr tcc_c_pipeline(global_ctx_t& globals, bool is_runtime, vs::ui_base* obj, const char* src, void* ctx, void(*error_fn)(void*,const char*), bool compact, const char *link_with){ auto script = std::make_shared(); //This part is a bit of a mess. @@ -50,8 +78,8 @@ std::shared_ptr tcc_c_pipeline(bool is_runtime, vs::ui_base* obj, const cha script->set_out_type(tcc::memory); - script->add_sysinclude_path((globals::path_env.root.location+"./bindings/native/tcc/include").c_str()); - script->add_include_path((globals::path_env.root.location+"./bindings/native/include").c_str()); + script->add_sysinclude_path((globals.path_env.root.location+"./bindings/native/tcc/include").c_str()); + script->add_include_path((globals.path_env.root.location+"./bindings/native/include").c_str()); //script->add_lib("ld"); //script->add_lib("tcc1"); @@ -81,6 +109,8 @@ std::shared_ptr tcc_c_pipeline(bool is_runtime, vs::ui_base* obj, const cha script->add_sym("vs_set", (void *)+[](ui_base* w,const char* k, const void* v){if(w==nullptr)return -1;std::string tmp = std::string("#s_")+k;return w->use_setter(w->resolve_symbol_local(tmp.c_str(), false), v);}); script->add_sym("vs_get", (void *)+[](ui_base* w,const char* k, void** v){if(w==nullptr)return -1;std::string tmp = std::string("#g_")+k;return w->use_getter(w->resolve_symbol_local(tmp.c_str(), false), v);}); + //Runtime functions + script->add_sym("itoa", (void *)itoa); // Fragments of stdlib // --- cstring @@ -144,7 +174,7 @@ std::shared_ptr tcc_c_pipeline(bool is_runtime, vs::ui_base* obj, const cha if(on_compiled!=nullptr)on_compiled(); auto on_static_test = (int(*)())script->get_sym("static_test"); - if(on_static_test!=nullptr && globals::env.computed_policies.testing)on_static_test(); + if(on_static_test!=nullptr && globals.env.computed_policies.testing)on_static_test(); if(obj!=nullptr){ //Apply the environment for single use scripts. @@ -220,4 +250,6 @@ std::shared_ptr> tcc_c_pipeline_apply(const std::shared_ptr #undef LIBT } -} \ No newline at end of file +} + +#endif \ No newline at end of file diff --git a/src/ui-base.cpp b/src/ui-base.cpp index 97f8afa9..e3fe1453 100644 --- a/src/ui-base.cpp +++ b/src/ui-base.cpp @@ -6,7 +6,6 @@ #include #include "pipelines/quickjs-js.hpp" -#include "quickjs.h" #include "ui-frame.hpp" #include "utils/strings.hpp" @@ -348,6 +347,7 @@ int ui_base::use_callback(const symbol_ret_t& sym, ui_base * node){ } } else if(sym.found_at->get_mode()==frame_mode_t::QUICKJS){ + #if VS_USE_QJS //TODO: Add support for quickjs script modules pipelines::quickjs_t* script = (pipelines::quickjs_t*)sym.found_at->script.get(); auto globalThis = JS_GetGlobalObject(script->ctx); @@ -355,6 +355,7 @@ int ui_base::use_callback(const symbol_ret_t& sym, ui_base * node){ JS_FreeValue(script->ctx, ret); JS_FreeValue(script->ctx, globalThis); return 0; + #endif } else{ //Callback type not supported yet. diff --git a/src/ui-tree.xml.cpp b/src/ui-tree.xml.cpp index 6c61bb2a..62328b6a 100755 --- a/src/ui-tree.xml.cpp +++ b/src/ui-tree.xml.cpp @@ -87,14 +87,15 @@ void ui_tree_xml::log(int severety, const void* _ctx, const char* str, ...){ //General XML loader for apps and components. //TODO: The caller node is a design flaw. We need to be given the list of props and slots. Not the full node which might not even exist. -int ui_tree_xml::load(const char* file, type_t type, const pugi::xml_node* caller_node, ui_base* caller_ui_node, const scoped_rpath_t* caller_path, const policies_t& base_policies) +int ui_tree_xml::load(const char* file, type_t type) { //TODO: As part of this process, policies should be aligned with what defined in the base config, //and not just one single set of options, so that we can pattern match paths. - policies.inherit(base_policies); + policies.inherit(globals.env.computed_policies); + if(parent!=nullptr)policies.inherit(parent->policies); - resolve_path resolver(policies,globals::path_env,(caller_path==nullptr)?globals::path_env.cwd:*caller_path); - auto buffer = fetcher(resolver,resolve_path::from_t::NATIVE_CODE,file); + resolve_path resolver(policies,globals.path_env,(parent==nullptr)?globals.path_env.cwd:parent->local); + auto buffer = fetcher(globals,resolver,resolve_path::from_t::NATIVE_CODE,file); if(std::get<0>(buffer)==resolve_path::reason_t::OK){ this->local=std::get<2>(buffer).base_dir(); @@ -102,8 +103,6 @@ int ui_tree_xml::load(const char* file, type_t type, const pugi::xml_node* calle } this->type = type; - this->caller_node=caller_node; - this->caller_ui_node=caller_ui_node; vs_log(severety_t::INFO, nullptr, "Requested loading of file `%s`", std::get<2>(buffer).as_string().data()); //TODO: Move this to cache later on, but for now just dump the result into a buffer @@ -144,9 +143,9 @@ int ui_tree_xml::load(const char* file, type_t type, const pugi::xml_node* calle pugi::xml_document datadoc; auto tplt = root_data.attribute("template"); //TODO: Adapt to use the proper syntax. I cannot remember which one was it - resolve_path resolver(policies,globals::path_env,this->local); + resolve_path resolver(policies,globals.path_env,this->local); - auto buffer = fetcher(resolver,resolve_path::from_t::NATIVE_CODE,tplt.as_string()); + auto buffer = fetcher(globals,resolver,resolve_path::from_t::NATIVE_CODE,tplt.as_string()); if(std::get<0>(buffer)!=resolve_path::reason_t::OK){ //TODO: error handling return 2; @@ -230,8 +229,8 @@ void ui_tree_xml::_build(const pugi::xml_node& root, ui_base* root_ui){ } else{ log(severety_t::INFO,root,"Loading component %s", src.as_string()); //TODO: How is it possible that this shows file:// in place of the actual one? - ui_tree_xml component_tree; - if(component_tree.load(src.as_string(),type_t::COMPONENT,&root,root_ui,&local, policies)!=0){ + ui_tree_xml component_tree(globals,this,root_ui,&root); + if(component_tree.load(src.as_string(),type_t::COMPONENT)!=0){ log(severety_t::INFO,root,"Loading failed, file cannot be opened %s", src); } else{ @@ -267,8 +266,8 @@ void ui_tree_xml::_build(const pugi::xml_node& root, ui_base* root_ui){ //Copy the content provided by the parent in place of the slot, or render its content if the parent has none. const auto& name = root.attribute("tag").as_string("default"); //The default slot. - if(this->caller_node!=nullptr){ - const auto& match = this->caller_node->find_child([&name](const pugi::xml_node& node){ + if(this->caller_xml_node!=nullptr){ + const auto& match = this->caller_xml_node->find_child([&name](const pugi::xml_node& node){ return (strcmp(node.name(),"inject")==0) && (strcmp(node.attribute("tag").as_string("default"),name)==0); }); @@ -286,7 +285,7 @@ void ui_tree_xml::_build(const pugi::xml_node& root, ui_base* root_ui){ } //DEBUG else if(strcmp(root.name(),strings.DEBUG_TAG)==0){ - globals::debug(root.attribute("key").as_string(""), root.attribute("value").as_string("")); + globals.debug(root.attribute("key").as_string(""), root.attribute("value").as_string("")); return; } //VIEWPORT @@ -342,8 +341,8 @@ void ui_tree_xml::_build(const pugi::xml_node& root, ui_base* root_ui){ else if(imports.contains(root.name())){ auto it = imports.find(root.name()); log(severety_t::INFO,root,"Loading component %s", it->second.c_str()); //TODO: How is it possible that this shows file:// in place of the actual one? - ui_tree_xml component_tree; - if(component_tree.load(it->second.c_str(),type_t::COMPONENT,&root,root_ui,&local, policies)!=0){ + ui_tree_xml component_tree(globals,this,root_ui,&root); + if(component_tree.load(it->second.c_str(),type_t::COMPONENT)!=0){ log(severety_t::INFO,root,"Loading failed, file cannot be opened %s", it->second.c_str()); } else{ @@ -405,7 +404,7 @@ void ui_tree_xml::_build_base_widget_extended_attr(const pugi::xml_node &root, u is_module=true; auto filename = this->fullname.as_string(); - auto found = globals::mem_storage.get({filename.c_str(),this->local_unique_counter+1,cache::resource_t::SCRIPT}); + auto found = globals.mem_storage.get({filename.c_str(),this->local_unique_counter+1,cache::resource_t::SCRIPT}); if(found!=nullptr){ current->set_mode(((cache::script_t*)found->ref.get())->mode); current->attach_script(((cache::script_t*)found->ref.get())->script,is_module); @@ -423,7 +422,7 @@ void ui_tree_xml::_build_base_widget_extended_attr(const pugi::xml_node &root, u auto link_with = doc.first_child().attribute("link-with").as_string(nullptr); std::string tmp_link; if(link_with!=nullptr){ - resolve_path resolver(policies,globals::path_env,local); + resolve_path resolver(policies,globals.path_env,local); auto computed_path = resolver(resolve_path::from_t::NATIVE_CODE,link_with); if(computed_path.first==resolve_path::reason_t::OK){ //TODO: For now I am assuming it is on the fs. I should resolve it to tmp if remote for example @@ -437,7 +436,8 @@ void ui_tree_xml::_build_base_widget_extended_attr(const pugi::xml_node &root, u if (mode == frame_mode_t::NATIVE || mode == frame_mode_t::AUTO) { const auto &lang = root.attribute("lang").as_string(mode==frame_mode_t::NATIVE?"c":""); if (strcmp(lang, "c") == 0) { - auto compiler = pipelines::tcc_c_pipeline_xml(true, is_module?nullptr:current, root, compact, (link_with==nullptr)?nullptr:tmp_link.c_str()); + #ifdef VS_USE_TCC + auto compiler = pipelines::tcc_c_pipeline_xml(globals,true, is_module?nullptr:current, root, compact, (link_with==nullptr)?nullptr:tmp_link.c_str()); if(compiler!=nullptr){ current->set_mode(frame_mode_t::NATIVE); current->attach_script(compiler,is_module); @@ -447,17 +447,19 @@ void ui_tree_xml::_build_base_widget_extended_attr(const pugi::xml_node &root, u auto tmp = std::make_shared(cache::script_t{ compiler, symbols, frame_mode_t::NATIVE }); - globals::mem_storage.fetch_from_shared({this->fullname.as_string().c_str(),local_unique_counter+1,cache::resource_t::SCRIPT,false,false}, tmp); + globals.mem_storage.fetch_from_shared({this->fullname.as_string().c_str(),local_unique_counter+1,cache::resource_t::SCRIPT,false,false}, tmp); local_unique_counter++; } } continue; + #endif } } if (mode == frame_mode_t::QUICKJS || mode == frame_mode_t::AUTO) { const auto &lang = root.attribute("lang").as_string(mode==frame_mode_t::QUICKJS?"js":""); if (strcmp(lang, "js") == 0) { - auto compiler = pipelines::qjs_js_pipeline_xml(true, is_module?nullptr:current, root, (link_with==nullptr)?nullptr:tmp_link.c_str()); + #ifdef VS_USE_QJS + auto compiler = pipelines::qjs_js_pipeline_xml(globals,true, is_module?nullptr:current, root, (link_with==nullptr)?nullptr:tmp_link.c_str()); if(compiler!=nullptr){ current->set_mode(frame_mode_t::QUICKJS); current->attach_script(compiler,is_module); @@ -467,11 +469,12 @@ void ui_tree_xml::_build_base_widget_extended_attr(const pugi::xml_node &root, u auto tmp = std::make_shared(cache::script_t{ compiler, symbols, frame_mode_t::QUICKJS }); - globals::mem_storage.fetch_from_shared({this->fullname.as_string().c_str(),local_unique_counter+1,cache::resource_t::SCRIPT,false,false}, tmp); + globals.mem_storage.fetch_from_shared({this->fullname.as_string().c_str(),local_unique_counter+1,cache::resource_t::SCRIPT,false,false}, tmp); local_unique_counter++; } } continue; + #endif } } if (mode == frame_mode_t::LUA || mode == frame_mode_t::AUTO) { @@ -567,7 +570,7 @@ void ui_tree_xml::_build_base_widget_extended_attr(const pugi::xml_node &root, u //For components add to its direct children the attributes coming from above if(strcmp(root.parent().name(),strings.COMPONENT_TAG)==0){ - for (const auto &i : this->caller_node->attributes()) { + for (const auto &i : this->caller_xml_node->attributes()) { //Exclude src as it was consumed in here. if(strcmp(i.name(),"src")!=0)props.insert_or_assign(i.name(), i.value()); } diff --git a/src/utils/env.cpp b/src/utils/env.cpp index ae51c3a7..93019e9a 100644 --- a/src/utils/env.cpp +++ b/src/utils/env.cpp @@ -8,9 +8,18 @@ #include #include + +#ifdef VS_USE_QJS #include +#endif + +#ifdef VS_USE_TCC #include "subprojects/libtcc/config.h" +#endif + +#ifdef VS_USE_WAMR #include "subprojects/wamr/core/version.h" +#endif //#include #ifdef HAS_CURL @@ -25,7 +34,7 @@ namespace vs{ //TODO: For now this is linux only. I will need to be expanded to support more os -path_env_t mk_env(const char* arg0,const char* arg1){ +path_env_t mk_env(global_ctx_t& globals, const char* arg0,const char* arg1){ path_env_t main_env; static char buffer[1024]; if(getcwd(buffer,1023)==nullptr){throw "Unable to get CWD";} @@ -60,7 +69,7 @@ path_env_t mk_env(const char* arg0,const char* arg1){ main_env.tmp_path={rpath_type_t::FS,"/tmp/"}; //Finally compute path for the requested file - resolve_path resolver(globals::env.computed_policies,main_env,main_env.cwd); + resolve_path resolver(globals.env.computed_policies,main_env,main_env.cwd); { auto t = resolver(resolve_path::from_t::NATIVE_CODE,arg1); if(t.first!=resolve_path::reason_t::OK)exit(2); //TODO: Handle case @@ -71,16 +80,34 @@ path_env_t mk_env(const char* arg0,const char* arg1){ js_rt_t::js_rt_t(){ + #if VS_USE_QJS auto tmp=JS_NewRuntime(); //TODO define limits somewhere //JS_SetMemoryLimit(tmp, 80 * 1024); //JS_SetMaxStackSize(tmp, 10 * 1024); rt=tmp; + #endif +} + +js_rt_t::~js_rt_t(){ + #if VS_USE_QJS + JS_FreeRuntime((JSRuntime*)rt); + #endif } -js_rt_t::~js_rt_t(){JS_FreeRuntime((JSRuntime*)rt);} + void* js_rt_t::operator()(){return rt;} +vs_test_debug_t::vs_test_debug_t(){auto file=getenv("VS_DEBUG_FILE");if(file!=nullptr)fd=fopen(file,"w+");} +vs_test_debug_t::~vs_test_debug_t(){if(fd!=nullptr)fclose(fd);} + +void vs_test_debug_t::operator()(const char* field, const char* value){ + if(fd==nullptr)return; + else{ + auto now = std::chrono::system_clock::now(); + fprintf(fd,"%s\t%s\t%ld\n",field,value,std::chrono::duration_cast(now.time_since_epoch()).count()); + } +} @@ -121,18 +148,30 @@ void prepare_db(){ versions_t get_versions(){ versions_t tmp; + tmp.vs=vs_version(); # ifdef HAS_CURL - tmp.curl=curl_version(); + tmp.curl = curl_version(); # else - tmp.curl="Not installed"; + tmp.curl = "Not installed"; +# endif + tmp.fltk = std::to_string(FL_API_VERSION); + tmp.libuv = "Not installed";//uv_version_string(); + tmp.sqlite = sqlite3_libversion(); +# if VS_USE_TCC + tmp.tcc = TCC_VERSION; +# else + tmp.tcc = "Not installed"; +# endif +# if VS_USE_QJS + tmp.quickjs=JS_GetVersion(); +# else + tmp.quickjs = "Not installed"; +# endif +# if VS_USE_WAMR + tmp.wamr= WAMR_VERSION; +# else + tmp.wamr = "Not installed"; # endif - tmp.fltk=std::to_string(FL_API_VERSION); - tmp.libuv="---";//uv_version_string(); - tmp.sqlite=sqlite3_libversion(); - tmp.tcc= TCC_VERSION; - tmp.quickjs=JS_GetVersion(); - tmp.vs=vs_version(); - tmp.wamr= WAMR_VERSION; return tmp; } diff --git a/subprojects/libhttp.wrap b/subprojects/libhttp.wrap new file mode 100644 index 00000000..466aebc5 --- /dev/null +++ b/subprojects/libhttp.wrap @@ -0,0 +1,5 @@ +[wrap-git] +url = https://github.com/lazy-eggplant/libhttp +revision = HEAD +depth = 1 +method = meson \ No newline at end of file