diff --git a/.gitignore b/.gitignore index 31edad31..3b4b5c66 100644 --- a/.gitignore +++ b/.gitignore @@ -13,7 +13,7 @@ *.a -# oocmake ignores +# cmakepp ignores tmp bin release diff --git a/README.md b/README.md index 2f2714ea..224418e8 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![GitHub stars](https://img.shields.io/github/stars/toeb/cmakepp.svg?)](https://github.com/toeb/cmakepp/stargazers) [![GitHub forks](https://img.shields.io/github/forks/toeb/cmakepp.svg)](https://github.com/toeb/cmakepp/network) [![GitHub issues](https://img.shields.io/github/issues/toeb/cmakepp.svg)](https://github.com/toeb/cmakepp/issues) -[![biicode block](https://img.shields.io/badge/toeb%2Fcmakepp-DEV%3A0-yellow.svg)](https://www.biicode.com/toeb/cmakepp) +[![Build Status](https://webapi.biicode.com/v1/badges/toeb/toeb/cmakepp/master?pleasegithubstopcachingmycoolbadge)](https://www.biicode.com/toeb/cmakepp) [![Project Stats](https://www.ohloh.net/p/cmakepp/widgets/project_thin_badge.gif)](https://www.ohloh.net/p/cmakepp) @@ -19,6 +19,7 @@ Look through the files in the package. Most functions will be commented and the `cmakepp` has a lot of different functions. I tried to subdivide them into some meaningful sections. +* [Creating Checksums](cmake/checksum/README.md) * [Collections](cmake/collections/README.md) * [Date/Time](cmake/datetime/README.md) * [Events](cmake/events/README.md) @@ -26,6 +27,7 @@ Look through the files in the package. Most functions will be commented and the * [Functions](cmake/function/README.md) * [Logging Functions](cmake/log/README.md) * [Maps - Structured Data in CMake](cmake/map/README.md) +* [Navigation Functions](cmake/navigation/README.md) * [Objects ](cmake/object/README.md) * [Package Management](cmake/package/README.md) * [User Data](cmake/persistence/README.md) @@ -59,6 +61,8 @@ I have developed some samples to show off `cmakepp`'s capabilities. Here you can * [Creating a Compressed Package](samples/04-create-simple-compressed-package/README.md) * [Creating and Installing a Package with an Install Hook](samples/05-create-install-simple-package-with-install-script/README.md) * [Installing and Using Projects with vanilla `CMake`](samples/06-vanilla-cmake-project-with-install/README.md) +* [Adding Boost to you `CMake` project](samples/07-adding-boost-by-package-manager/README.md) +* [Download, Build and Use `jsoncpp`](samples/09-download-build-use-jsoncpp/README.md) @@ -154,6 +158,7 @@ I would be very happy If you choose to contribute to `cmakepp`. You can open any If you want to change something you may send pull requests through github. Normally I will check them quickly and `travis-ci` will build them. I suggest you run all tests using the sublime project before you create a pull request to see if anything breaks. (the master branch will have to pass the tests) +Also if you want to support me financially for all the hardwork - consider donating a couple of $ Click here to lend your support to: cmakepp  and make a donation at pledgie.com ! # Developer Guidlines @@ -267,7 +272,7 @@ This is possible by overwriting CMakes default return() function with a macro. I "Host":"httpbin.org", "User-Agent":"curl/7.16.1" }, - "origin":"85.180.184.251", + "origin":"87.157.218.55", "url":"http://httpbin.org/get?key=value" } diff --git a/README.md.in b/README.md.in index 0d6d858d..a106bceb 100644 --- a/README.md.in +++ b/README.md.in @@ -5,7 +5,7 @@ [![GitHub stars](https://img.shields.io/github/stars/toeb/cmakepp.svg?)](https://github.com/toeb/cmakepp/stargazers) [![GitHub forks](https://img.shields.io/github/forks/toeb/cmakepp.svg)](https://github.com/toeb/cmakepp/network) [![GitHub issues](https://img.shields.io/github/issues/toeb/cmakepp.svg)](https://github.com/toeb/cmakepp/issues) -[![biicode block](https://img.shields.io/badge/toeb%2Fcmakepp-DEV%3A0-yellow.svg)](https://www.biicode.com/toeb/cmakepp) +[![Build Status](https://webapi.biicode.com/v1/badges/toeb/toeb/cmakepp/master?pleasegithubstopcachingmycoolbadge)](https://www.biicode.com/toeb/cmakepp) [![Project Stats](https://www.ohloh.net/p/cmakepp/widgets/project_thin_badge.gif)](https://www.ohloh.net/p/cmakepp) @@ -111,6 +111,7 @@ I would be very happy If you choose to contribute to `cmakepp`. You can open any If you want to change something you may send pull requests through github. Normally I will check them quickly and `travis-ci` will build them. I suggest you run all tests using the sublime project before you create a pull request to see if anything breaks. (the master branch will have to pass the tests) +Also if you want to support me financially for all the hardwork - consider donating a couple of $ Click here to lend your support to: cmakepp  and make a donation at pledgie.com ! # Developer Guidlines diff --git a/README.md.old b/README.md.old deleted file mode 100644 index da724b72..00000000 --- a/README.md.old +++ /dev/null @@ -1,2202 +0,0 @@ -![cmakepp logo](https://raw.githubusercontent.com/toeb/cmakepp/master/logo.png "cmakepp logo") - -## A CMake Enhancement Suite -[![Travis branch](https://img.shields.io/travis/toeb/cmakepp/master.svg)](https://travis-ci.org/toeb/cmakepp) -[![GitHub stars](https://img.shields.io/github/stars/toeb/cmakepp.svg?)](https://github.com/toeb/cmakepp/stargazers) -[![GitHub forks](https://img.shields.io/github/forks/toeb/cmakepp.svg)](https://github.com/toeb/cmakepp/network) -[![GitHub issues](https://img.shields.io/github/issues/toeb/cmakepp.svg)](https://github.com/toeb/cmakepp/issues) -[![biicode block](https://img.shields.io/badge/toeb%2Fcmakepp-DEV%3A0-yellow.svg)](https://www.biicode.com/toeb/cmakepp) -[![Project Stats](https://www.ohloh.net/p/cmakepp/widgets/project_thin_badge.gif)](https://www.ohloh.net/p/cmakepp) - - -# Installing - -You have multiple options for install `cmakepp` the only prerequisite for all options is that cmake is installed with version `>=2.8.7`. - - -* [Install by Console](#install_console) - Recommended -* Use the [Biicode Block](https://www.biicode.com/toeb/cmakepp) -* [Download a release](https://github.com/toeb/cmakepp/releases) and include it in your cmake script file - If you do not want to run the tests or have access to the single function files this option is for you - - [Manually setup aliases](#install_aliases) -* Clone the repository and include `cmakepp.cmake` in your `CMakeLists.txt` (or other cmake script) - -# Usage -Look through the files in the package. Most functions will be commented and the other's usage can be inferred. All functions are avaiable as soon as you include the cmakepp.cmake file. - -# Testing -To test the code (alot is tested but not all) run the following in the root dir of cmakepp *this takes long :)* - -``` -cmake -P build/script.cmake -``` - -# Feature Overview - -`cmakepp` is a general purpose library for cmake. It contains functionality that was missing in my opinion and also wraps some cmake functionality to fit to the style of this library. - -* Features - * [interactive cmake console](#icmake) (`cmake -P icmake.cmake`) - * [lists](#lists) - common and usefull list and set operations. - - [ranges](#ranges) range based access to lists - * [maps](#maps) - map functions and utility functions (nested data structures for cmake) - * graph algorithms - * serialization/deserialization - * [json](#json) - * [quickmap format](#quickmap) (native to cmake) - * [xml](#xml) - * [assign](#assign) universal assign ease of use for maps and functions - * [expression syntax](#expr). - * `obj("{id:1,prop:{hello:3, other:[1,2,3,4]}}")` -> creates the specified object - * [eval](#eval) - evaluates cmake code and is the basis of many advanced features - * [shell](#shell) - "platform independent" shell script execution - * [aliases](#aliases) - platform independent shell aliases - * [console](#console) - functions for console input and outputf - * [filesystem](#filesystem) - directory and file functions with close relations to bash syntax - * [mime types](#mimetypes) - mime type based file handling - * [compression/decompression](#compression) - compressing and decompressing tgz and zip files - * [command execution](#execute) simplifying access to exectables using the shell tools. - * [cmake tool compilation](#tooling) simple c/c++ tools for cmake - * debugging - * some convenience functions - * `breakpoint()` - stops execution at specified location and allows inspection of cmake variables, execution of code (if `-DDEBUG_CMAKE` was specified in command line) - * [version control systems](#vcs) - * `hg()` convenience function for calling mercurial vcs - * `git()` convenience function for calling git vcs - * `svn()` convenience function for calling subversion vcs - * utility methods for working with the different systems - * [package search and retrieval](#packages) - * [cmake](#cmake) calling cmake from cmake. - * [date/time](#datetime) - * function for getting the correct date and time on all OSs - * get milliseconds since epoch - * [events](#events) allows registering event handlers and emitting events - * [Windows Registry](#windowsregstry) - * `reg()` shorthand for working with windows registry command line interface - * read write manipulate registry values - * query registry keys for values - * [string functions](#stringfunctions) - advanced string manipulation - * [URIs](#uris) - Uniform Resource Identifier parsing and formatting - * [HTTP client](#http_client) - Perform HTTP/GET HTTP/PUT request with pure cmake. - * [user data](#userdata) - persists and retrieves data for the current user (e.g. allowing cross build/ script configuration) - * functions - * [returning values](#return) - * define dynamic functions (without cluttering the namespace) - * call functions dynamically (basically allowing `${functionName}(arg1 arg2 ...)` by typing `call(${functionName}(arg1 arg2 ...))`) - * set a variable to the result of functions `rcall(result = somefunction())` - * lambda functions (a shorthand syntax for defining inline functions. `(var1,var2)-> do_somthing($var1); do_something_else($var2)` - * import functions (from files, from string, ...) - * [objects](#objects) - object oriented programming with prototypical inheritance, member functions - * [process management](#process_management) - platform independent forking, waiting, controlling separate process from cmake - * [Targets](#targets) - * [access to a list of all defined targets](#target_list) - * easier access to target properties - * - * [implementation notes](#implementation_notes) - - -*NOTE: the list is still incomplete* - - -# Install by Console - -For ease of use I provide you with simple copy paste code for your console of choice. These scripts download the `install.cmake` file and execute it. This file in turn downloads `cmakepp` and adds itself to your os (creating aliases and setting a environment variable - which allow you to use [icmake](#icmake) and [cmakepp cli](#cmake_cli) from the console). - -*Bash* -``` -#!bin/bash -wget https://raw.github.com/toeb/cmakepp/master/install.cmake && cmake -P install.cmake && rm install.cmake -``` - -*Powershell* -``` -((new-object net.webclient).DownloadString('https://raw.github.com/toeb/cmakepp/master/install.cmake')) |` -out-file -Encoding ascii install.cmake; ` -cmake -P install.cmake; ` -rm install.cmake; - - -``` - - -# Manually setting up aliases - - - -# Refs - -CMake has a couple of scopes every file has its own scope, every function has its own scope and you can only have write access to your `PARENT_SCOPE`. So I searched for simpler way to pass data throughout all scopes. My solution is to use CMake's `get_property` and `set_property` functions. They allow me to store and retrieve data in the `GLOBAL` scope - which is unique per execution of CMake. It is my RAM for cmake - It is cleared after the programm shuts down. - -I wrapped the get_property and set_property commands in these shorter and simple functions: - -``` -ref_new() # returns a unique refernce (you can also choose any string) -ref_set(ref [args ...]) # sets the reference to the list of arguments -ref_get(ref) # returns the data stored in - -# some more specialized functions -# which might be faster in special cases -ref_setnew([args ...]) # creates, returns a which is set to -ref_print() # prints the ref -ref_isvalid() # returns true iff the ref is valid -ref_istype( ) # returns true iff ref is type -ref_gettype() # returns the (if any) of the ref -ref_delete() # later: frees the specified ref -ref_append( [args ...])# appends the specified args to the 's value -ref_append_string( ) # appends to 's value -``` - -*Example*: -``` - # create a ref - ref_new() - ans(ref) - assert(ref) - - # set the value of a ref - ref_set(${ref} "hello world") - -# retrieve a value by dereferencing -ref_get(${ref}) -ans(val) -assert(${val} STREQUAL "hello world") - -# without generating the ref: -ref_set("my_ref_name" "hello world") -ref_get("my_ref_name") -ans(val) -assert(${val} STREQUAL "hello world") -``` - - -# Maps - -Maps are very versatile and are missing dearly from CMake in my opinion. Maps are references as is standard in many languages. They are signified by having properties which are adressed by keys and can take any value. - -Due to the "variable variable" system (ie names of variables are string which can be generated from other variables) it is very easy to implement the map system. Under the hood a value is mapped by calling `ref_set(${map}.${key})`. - - -## Functions and Datatypes -Using refs it easy to implement a map datastructure: -``` -map_new() # returns a unique reference to a map -map_get(map key) # returns the value of map[key], fails hard if key does not exist -map_has(map key) # returns true iff map[key] was set -map_set(map key [arg ...]) # sets map[key] -map_keys(map) # returns all keys which were set -map_tryget(map key) # returns the stored value or nothing: "" -map_sethidden(map key) # sets a field in the map without adding the key to the keys collection -map_remove() - -# some specialized functions -map_append() -map_append_string() - -``` - -* `mm():` creates a map from any kind of structured data - -## Map Iterators - -For a more intuitive way to work with maps I developed a `map_iterator` which which allows forward iteration of all maps. The syntax is held simple so that you can quickly go through a map as you can see in the following example: - -### Example - -*Iterate through a maps's key/value pairs and prints them* - -``` -mm(mymap = "{a:1,b:2,c:3}") -map_iterator(${mymap}) -ans(it) -while(true) - map_iterator_break(it) - # you have access to ${it.key} and ${it.value} - message("${it.key} = ${it.value}") -endwhile() -``` -*output* -``` -a = 1 -b = 2 -c = 3 -``` - -### Functions and Datatypes - -* ` ::= *internal data*` contains data which the iterator functions use. -* ` ::= ` a variable which contains an iterator -* `map_iterator(): ` creates a map iterator for the specied map -* `map_iterator_next():` returns true if the iterator could be advanced to the next key, also sets the variables `.key` and `.value` in the current scope -* `map_iterator_current():` also sets `.key` and `.value` -* `map_iterator_break()` only usable inside a loop (normally a while loop) it calls `break()` when the iterator has ended also sets `.key` and `.value` - -## Easy map handling with `assign()` - -Using the map and list functions can be cumbersome. Therefore I have added a universal function called `assign()` It allows you to use statements known from other programming languages. - -The easiest way to illustrate the usefullness is by example: - -*Examples* - -``` -## assign the string value 3 to x - single quotes indicate a value -assign(x = '3') -assert("${x}" EQUAL 3) - -## assign a string containing spaces to x -assign(x = "'hello there!'") -assert("${x}" STREQUAL "hello there!" ) - -## assign a literal to x - simplification of the above -assign(x "hello world" "how are you") # notice missing '=' -assert("${x}" EQUALS "hello world" "how are you") - -## assign the result of a function call to x -assign(x = string_length("abcde")) -assert("${x}" EQUAL 5) - -## assign x the result of an expression -assign(x = "{id:1}") # the strign {id:1} is parsed into a map -assertf("{x.id}" EQUAL 1) - -## append a value to x -set(x) -assign(x[] = '1') -assign(x[] = '2') -assign(x[] = '3') -assign(x[] = '4') -assert(${x} EQUALS 1 2 3 4) - -## reverse x -set(x 1 2 3 4) -assign(x = x[$:0:-1]) # read assign x at end to 0 in -1 increments to x -assert(${x} EQUALS 4 3 2 1) - -## replace a range of x -set(x 1 2 3 4) -assign(x[1:2] = '5') -assert(${x} EQUALS 1 5 4) - -## create a object path if it does not exist by prepending '!' -set(x) -assign(!x.y.z = '3') -assert_matches("{y:{z:3}}" "${x}") - -... - -``` - - -## Naive Xml Deserialization - -Xml Deserialization is a complex subject in CMake. I have currently implemented a single naive implementation based on regular expressions which *does not* allow recursive nodes (ie nodes with the same tag being child of one another). - - - -### Functions - -* `xml_node( )->{ tag:, value:, attrs:{:}}` creates a naive xml node representation. -* `xml_parse_nodes( )-> list of xml_nodes` this function looks for the specified tag in the string (matching every instance(no recursive tags)) it parses the found nodes attributes and value (innerXml). You can then use `nav()`, `map_get()` etc functions to parse the nodes - - - - -## Json Serialziation and Deserialization - -I have written five functions which you can use to serialize and deserialize json. - -### Functions - -* `json()` - - transforms the specified object graph to condensed json (no superfluos whitespace) - - cycles are detected but not handled. (the property will not be set if it would cause a cycle e.g. map with a self reference would be serialized to `{"selfref":}` which is incorrect json... which will be addressed in the future ) - - unicode is not transformed into `\uxxxx` because of lacking unicode support in cmake -* `json_indented()` - - same as `json()` however is formatted to be readable ie instead of `{"var":"val","obj":{"var":["arr","ay"]}}` it will be -``` -{ - "var":"var", - "obj":[ - "arr", - "ay" - ] -} -``` -* `json_deserialize()` - - deserialized a json string ignoring any unicode escapes (`\uxxxx`) -* `json_read()` - - directly deserializes af json file into a map -* `json_write( )` - - write the map to the file - -### Caveats -As can be seen in the functions' descriptions unicode is not support. Also you should probably avoid cycles. - -### Caching -Because deserialization is extremely slow I chose to cache the results of deserialization. So the first time you deserialize something large it might take long however the next time it will be fast (if it hasn't changed). -This is done by creating a hash from the input string and using it as a cache key. The cache is file based using Quick Map Syntax (which is alot faster to parse since it only has to be included by cmake). - -## Quick Map Syntax -To quickly define a map in cmake I introduce the quick map syntax which revolves around these 5 functions and is quite intuitive to understand: -``` -map([key]) # creates, returns a new map (and parent map at to the new map) -key() # sets the current key -val([arg ...]) # sets the value at current map[current key] to -kv( [arg ...]) # same as writing key() LF val([arg ...]) -end() # finishes the current map and returns it -``` - -*Example* -Here is an example how to use this syntax -``` -# define the map -map() - key(firstname) - value(Tobias) - key(lastname) - value(Becker) - value(projects) - map() - kv(name cmakepp) - kv(url https://github.org/toeb/cmakepp) - end() - map() - key(name) - value(cutil) - key(url) - value(https://github.org/toeb/cutil) - end() - end() - map(address) - key(street) - value(Musterstrasse) - key(number) - value(99) - end() -end() -# get the result -ans(themap) -# print the result -ref_print(${themap}) -``` - -*Output* -``` -{ - "firstname":"Tobias", - "lastname":"Becker", - "projects":[ - { - "name":"cmakepp", - "url":"https://github.org/toeb/cmakepp" - }, - { - "name":"cutil", - "url":"https://github.org/toeb/cutil" - } - ] - "address":{ - "street":"Musterstrasse", - "number":"99" - } -} - -``` - - -# Functions - -CMake is a function oriented language. Every line in a cmake script is a function just a function call. It is the only available statement. CMake does not allow dynamic function calling (ie calling a function which you first know at runtime). This has problem and some further funcitonality issues are addressed in this section. - -Functions in cmake are not variables - they have a separate global only scope in which they are defined. -*A Note on Macros* Macros are also functions. They do not have their own scope and evaluate arguments differently. They will more likely than not have unintended side effects because of the way the are evaluated. There are valid reasons to use macros but if you do not know them, you SHOULD NOT use macros... - -## Functions and Datatypes - -* Datatypes - * ` ::= ` any valid cmake code - * ` ::= ` a cmake script file containing a single function - * ` :: ` a string containing a single function - * ` ::= ` a file containing valid cmake code - * ` ::=()` a function call can be evaluated to a valid cmake code line which executes the function specified - * `` ::= ` any cmake function or macro name for which `if(COMMAND )` evaluates to true. This can be directly called - * `` ::= ||||| a function?! can be any type of code which somehow evaluates to a function - * ` ::= {type:, name:, args:, code:|}` a map containing information on a specific function. if possible the info map contains the original source code of the function -* Functions - * `eval()` executes the given cmake code. If the code returns something (see return) the result will be available after the `eval()` call using `ans()` - * `eval_ref()` executes the given code. Since this is a macro the code is passed as a variable name (reference) to suppress automatic variable expansion. This allows you to eval code which uses the `set(x y PARENT_SCOPE)` - * `function_new()` returns a unqiue name for a function. - * `is_function():` returns true if the specified value is a function - * `function_import( as >)` imports a function under the specified name - * `call()` calls a function and returns the return value of the call. - * `rcall( = )` calls a function and sets the specified identifier to the return value of the call. - * `function_info():` returns info on name, arguments, type of function - * `wrap_platform_specific_function(>` a helper method which allows platform the correct choice for platform specific functions. Consider getting an environment variable like the home directory. Under Linux this is `$ENV{HOME}` under Windows this is `$ENV{HOME_DRIVE}$ENV{HOME_DIR}` depending on the os the result is 'the same' but you get it is different. this function looks for specialized functions and imports the most fitting one as ``. If no function is found which matches the platform a dummy function is implemented which throws a `FATAL_ERROR` detailing how you can alleviate the missing implementation problem. - * `curry` - * `bind` - * Lambdas - * `` string of code similar to cmake. a typical lambda looks like this `(a)-> return_truth($a STREQUAL "hello")` - * `lambda():` - * `lambda_import()` - * `lambda_parse()` - - -## Examples - -``` -``` - - -# Objects - -Objects are an extension of the maps. They add inheritance, member calls and custom member operations to the concept of maps. I split up maps and objects because objects are a lot slower (something like 2x-3x slower) and if you do not need objects you should not use them (handling 1000s of maps is already slow enough). The reason for the performance loss is the number of function calls needed to get the correct virtual function/property value. - - -## Functions and Datatypes - -These are the basic functions which are available (there are more which all use these basic ones): - -* Basic Object functions - Functions on which all other object functions base - - ` := ` a type is represented by a globally unique cmake function. This function acts as the constructor for the type. In this constructor you define inheritance, properties and member functions. The type may only be defined once. So make sure the function name is and stays unique ie. is not overwritten somewhere else in the code. - - ` := ` an object is the instance of a type. - - ` := ` a string which identifies the member of an object - - `obj_new():` creates the instance of a type. calls the constructor function specified by type and returns an object instance - - `obj_delete():` deletes an object instance. You MAY call this. If you do the desctructor of an object is invoked. This function is defined for completeness sake and can only be implemented if CMake script changes. So don't worry about this ;). - - `obj_set( >):` sets the object's property identified by `` to the specified value. *the default behaviour can be seen in `obj_default_setter(...)` and MAY be overwridden by using `obj_declare_setter(...)`* - - `obj_get( ):` gets the value of the object's property identified by `` *the default behaviour MAY be overwridden b using `obj_declare_getter`* - - `obj_has( ):` returns true iff the object has a property called `` - - `obj_keys():` returns the list of available members - - `obj_member_call( >):` calls the specified member with the specified arguments and returns the result of the operation -* Most `obj_*` functions are also available in the shorter `this-form` so `obj_get(> ...)` can also be used inside a member function or constructor by using the function `this_get(...)`. This is achieved by forwarding the call from `this_get(...)` to `obj_get(${this} ...)` - -## Overriding default behaviour - -As is possible in JavaScript an Python I let you override default object operations by specifying custom member functions. You may set the following hidden object fields to the name of a function which implements the correct interface. You can see the default implementations by looking at the `obj_default_*` functions. To ensure ease of use I provided functions which help you correctly override object behaviour. - -* reserved hidden object fields - * `__setter__ : ( ):` override the set operation. Return value may be anything. the default is void - * `__getter__ : ( ):` override the get operation. expects the return value to be the value of the object's property identified by `` - * `__get_keys__ : ():` override the operation which returns the list of available keys. It is expected that all keys returned will are valid properties (they exist). - * `__has__ : ( ):` overrides the has operation. MUST return true iff the object has a property called `` - * `__member_call__ : ( >):>` this operation is invoked when `obj_member_call(...)` is called (and thus also when `call, rcall, etc` is called) overriding this function allows you to dispatch a call operation to the object member identified by `` with the specified `args` it should return the result of the specified operation. The `this` variable is always set to the object instance current instance. - * `__cast__` @todo -* helper functions - * `obj_declare_getter()` - * `obj_declare_setter()` - * `obj_declare_call()` - * `obj_declare_member_call()` - * `obj_declare_get_keys()` - * `obj_declare_has_key()` @todo - * `obj_declare_cast()` @todo - - -``` -new([Constructor]) returns a ref to a object -obj_get(obj) -obj_set(obj) -obj_has(obj) -obj_owns(obj) -obj_keys(obj) -obj_ownedkeys(obj) -obj_call(obj) -obj_member_call(obj key [args]) -obj_delete(obj) -``` - - - -## Example - -This is how you define prototypes and instanciate objects. - -The syntax seems a bit strange and could be made much easier with a minor change to CMake... Go Cmake Gods give me true macro power! (allow to define a macro which can call function() and another which contains endfunction()) /Rant - - - -``` -function(BaseType) - # initialize a field - this_set(accu 0) - # declare a functions which adds a value to the accumulator of this object - proto_declarefunction(accuAdd) - function(${accuAdd} b) - this_get(accu) - math_eval("${accu} + ${b}") - ans(accu) - this_set(accu "${accu}") - call(this.printAccu()) - return(${accu}) - endfunction() - - proto_declarefunction(printAccu) - function(${printAccu}) - this_get(accu) - message("value of accu: ${accu}") - endfunction() -endfunction() -function(MyType) - # inherit another type - this_inherit(BaseType) - # create a subtract from accu function - proto_declarefunction(accuSub) - function(${accuSub} b) - this_get(accu) - math_eval("${accu} - ${b}") - this_set(accu "${accu}") - call(this.printAccu()) - return(${accu}) - endfunction() - -endfunction() - -new(MyType) -ans(myobj) -rcall(result = myobj.add(3)) -# result == 3, output 3 -rcall(result = myobj.sub(2)) -# result == 1, output 1 -``` - -## Special hidden Fields -``` -__type__ # contains the name of the constructor function -__proto__ # contains the prototype for this object -__getter__ # contains a function (obj, key)-> value -__setter__ # contains a function (obj, key,value) -__call__ # contains a function (obj [arg ...]) -__callmember__ # contains a function (obj key [arg ..]) -__not_found__ # gets called by the default __getter__ when a field is not found -__to_string__ # contains a function which returns a string representation for the object -__destruct__ # a function that is called when the object is destroyed -``` - - -# Parsing and handling semantic versions - - -A `semantic version` gives a specific meaning to it components. It allows software engineers to quickly determine weather versioned components of their software can be updated or if breaking changes could occur. - -On [semver.org](http://semver.org/) Tom Preston-Werner defines Semantic Versioning 2.0.0 which is recapped in the following. - -A `semantic version` is defined by is a version string which is in the following format whose components have a special semantic meaning in regard to the versioned subject: -``` - ::= (0|[1-9][0-9]*) - ::= [a-zA-Z0-9-]+ - ::= - ::= - ::= - ::= [.]* - ::= [.]* - ::= ..[-][+] - -``` -## Examples - -* `1.3.42-alpha.0+build-4902.nightly` -* `4.2.1` -* `0.0.0` - -## Description - -A `version number` may be any `0` or any positive integer. It may not have leading `0`s. - -`major` is a `version number`. - -A `version tag` non-empty alphanumeric string (also allowed: hyphen `-`) - -`prerelease` is a period `.` separated list of a `version tag`s. A version with `prerelease` is always of a lower order than a the same version without `prerelease`. - -`` is a list of period `.` separated `version tag`s with no meaning to the `semantic version`. It can be considered as user data. - -## Semantics - -`major` is a `version number`. `0` signifies that the public interface (API) of a package is not yet defined. The first major version `1` defines the public interface for a package. If the `major` version changes backwards incompatible changes have occured. - -`minor` is a `version number` which signifies a backwards compatible change in the public interface of a package. Updating a package to a new minor version MAY NOT break the dependee. - -`patch` is a `version number` which signifies a change in the internals of a package. ie bugfixes, inner enhancements. - -A `version number` SHOULD be incremented by `1` and `version number` the lower `version number`s are reset to zero. Incrementing a version with prerelease has to be done manually e.g. - -* increment `major` `1.23.1` => `2.0.0` -* increment `minor` `1.23.1` => `1.24.0` -* increment `patch` `1.23.1` => `1.23.2` - -## Semantic Version Object - -The sematic version object is the following map: - -``` -{ - "string":"..-+" - "numbers":"..", - "major":"", - "minor":"", - "patch":"", - "prerelease":"", - "metadata":"", - "tags":[ - , - , - ... - ], - "metadatas":[ - , - , - ... - ] - -} -``` -## Constraining Versions - -A `version constraint` constrains the set of all version to a subset. - -``` - ::= >=|<=|>|<|=|~|! - ::= "|" - ::= "," - ::= -``` - -* ` ::= <- , ` can be AND combined. `,` is the and operator and it has precedence before -* ` <- | `: ``s can be or combined -* `` -* `` -* `` -> `~` -* `` -* `` is a `` which may miss elements. These elements are ie `1` would correspond to `1.0.0` -* a `` can be one of the following - - `=` `` equals the specified version exactly - - `<` `` is less up to date then specified version - - `>` `` is greater than specified version - - `>=` `` is greater or equal to specified version evaluates to `> | =` - - `<=` `package_version` is less or equal than specified version evaluates to `< | =` - - `~` - - - - - -## Lazy Version - -A `lazy version` a less strict formulation of a `sematic version` -``` - ::= [whitespace]<|v||.|"">[whitespace] -``` - -A lazy version allows whitesspace and omission of `minor` and `patch` numbers. It also allows a preceding `v` as is common in many versioning schemes. - -A `lazy version` can be normalized to a strict `semantic version` by removing any whitespace around and in the version as well as the leading `v`, and filling up missing `major` and `minor` and `patch` version components with `0`. Normalizing an empty (or pure whitespace) string results in version `0.0.0` - -*Examples* -* `v1.3` => `1.3.0` -* `v1-alpha` => `1.0.0-alpha` -* `v1.3-alpha` => `1.3.0-alpha` -* `1` => `1.0.0` -* ` 1 ` => `1.0.0` -* ` `=> `0.0.0` -* -## Functions - -The following functions are usable for semantic versioning. - -* `semver()` parses a string or a semantic version object and returns a `` - - `semver(1.0)` => `{major:1,minor:0,patch:0}` - - `semver(2-alpha+build3.linux)` => `{major:2,minor:0,patch:0,prerelease:['alpha'],metadata:['build3','linux']}` - - `semver(2.3.1-beta.3+tobi.katha)` => `{major:2,minor:3,patch:1,prerelease:['beta','3'],metadata:['tobi','katha']}` -* `semver_compare( )` compares two semantiv versions. - - returns `-1` if left is more up to date - - returns `1` if right is more up to date - - returns `0` if they are the same -* `semver_higher( )` returns the semantic version which is higher. -* `semver_gt( )` returns true iff left semver is greater than right semver -* `semver_cosntraint_evaluate( )` returns true if `` satisfies `` - - `semver_constraint_evaluate("=0.0.1" "0.0.1")` -> true - - `semver_constraint_evaluate("=0.0.1" "0.0.2")` -> false - - `semver_constraint_evaluate("!0.0.1" "0.0.1")` -> false - - `semver_constraint_evaluate("!0.0.1" "0.0.2")` -> true - - `semver_constraint_evaluate(">0.0.1" "0.0.2")` -> true - - `semver_constraint_evaluate(">0.0.1" "0.0.1")` -> false - - `semver_constraint_evaluate("<0.0.1" "0.0.0")` -> true - - `semver_constraint_evaluate("<0.0.1" "0.0.1")` -> false - - `semver_constraint_evaluate("<=3,>2" "3.0.0")` -> true - - `semver_constraint_evaluate("<=3,>=2" "2.0.0")` -> true - - -## Caveats - -* parsing, constraining and comparing semvers is slow. Do not use too much (you can compile a semver constraint if it is to be evaluated agains many versions which helps a little with performance issues). - - -# Targets - -## target_list and project_list - -CMake as of version 2.8.7 does not support a list of all defined targets. -Therfore I overwrote all target adding functions `add_library`, `add_executable`, `add_custom_target`, `add_test`, `install` ... which now register the name of the target globally in a list. You can access this list by using the function `target_list()` which returns the list of known target names . Note that only targets defined before the `target_list()` call are known. - -I did the same thing for the `project()` command. - -## target debug functions - -To quickly get an overview of how your target is configured write `print_target()` it will print the json representation of the target as a message. - -To see how were all your targetes are type `print_project_tree` which will show the json representation of all your prrojects and targets. - -## target property functions - -accessing target properties made easier by the following functions - -* `target_get( )` returns the value of the target property -* `target_set( [ ...])` sets the value of the target property -* `target_append( [ ...])` appends the values to the current value of `` -* `target_has( )->bool` returns true iff the target has a property called `` - - - -# Windows Registry - - -Even though cmake and cmakepp are platform independent working with the windows registry is sometimes import/ e.g. setting environment variables. The cmake interface for manipulating registry values is not very nice (`cmake -E delete_regv` `write_regv`, `get_filename_component(result [HKEY_CURRENT_USER/Environment/Path] ABSOLUTE CACHE)` ) and hard to work with. Therefore I implemented a wrapper for the windows registry command line tool [REG](http://technet.microsoft.com/en-us/library/cc732643.aspx) and called it `reg()` it has the same call signature as `REG` with a minor difference: what is `reg add HKCU/Environment /v MyVar /f /d myval` is written `reg(add HKCU/Environment /v /MyVar /f /d myval)`. See also [wrap_executable](#executable) - - -## Availables Functions - - -Using this command I have added convinience functions for manipulating registry keys and values - -* `reg()` access to REG command line tool under windows (fails on other OSs) -* `reg_write_value(key value_name value)` writes a registry value (overwrites if it exists) -* `reg_read_value(key value_name)` returns the value of a registry value -* `reg_query_values(key)` returns a map containing all values of a specific registry key -* `reg_append_value(key value_name [args...])` append the specified values to the registries value -* `reg_prepend_value(key value_name [args...])` prepends the specified values to the registries value -* `reg_append_if_not_exists(key value_name [args ...]) appends the specifeid values to the registries value if they are not already part of it, returns only the values which were appended as result -* `reg_remove_value(key value_name [args ...])` removes the specified values from the registries value -* `reg_contains_value(key value_name)` returns true iff the registry contains the specified value -* `reg_query(key)` returns a list of `` objects which describe found values -* `` is a object with the fields key, value_name, value, type which describes a registry entry - - - -## Using windows registry functions example - - -``` -set(kv HKCU/Environment testValue1) - -## read/write -reg_write_value(${kv} "b;c") -reg_read_value(${kv}) -ans(res) -assert(EQUALS ${res} b c) - -## append -reg_append_value(${kv} "d") -reg_read_value(${kv}) -ans(res) -assert(EQUALS ${res} b c d) - -## prepend -reg_prepend_value(${kv} "a") -reg_read_value(${kv}) -ans(res) -assert(EQUALS ${res} a b c d) - - -## append if not exists -reg_append_if_not_exists(${kv} b c e f) -ans(res) -assert(res) -assert(EQUALS ${res} e f) -reg_read_value(${kv}) -ans(res) -assert(EQUALS ${res} a b c d e f) - - -## remove -reg_remove_value(${kv} b d f) -reg_read_value(${kv}) -ans(res) -assert(EQUALS ${res} a c e) - - -## contains -reg_contains_value(${kv} e) -ans(res) -assert(res) - - -## read key -reg_query_values(HKCU/Environment) -ans(res) -json_print(${res}) -assert(EQUALS DEREF {res.testValue1} a c e) -``` -# Date/Time - -I have provided you with a functions which returns a datetime object to get the current date and time on all OSs including windows. It uses file(TIMESTAMP) internally so the resolution is 1s. It would be possible to enhance this functionality to included milliseconds however that is more system dependent and therefore a decieded against it. - -`datetime()` currently only returns the local time. extending it to return UTC would be easy but I have not yet needed it - -In the future date time arithmetic might be added - -## Functions - -* `datetime()` returns the current date time as a `` -* `` an object containing the following fields: `yyyy` `MM` `dd` `hh` `mm` `ss` - - - - -# Eval - -`eval()` is one of the most central functions in cmakepp. It is the basis for many hacks an workarounds which cmakepp uses to enhance the scripting language. - -`eval` is not native to cmake (native eval would greatly increase the performance of this library) - -Internally it works by writing cmake script to a file and including it - -## Functions - -* `eval(code)` executes the specified code. `set(x z PARENT_SCOPE)` is not however you can return a value. - -## Examples - -Defining a Function and calling it - -``` -eval(" -function(say_hello name) - return(\"hello \${name}!\") # note: escape cmake double quotes and dolar sign in front of vars -endfunction() -") - -say_hello(Tobias) -ans(res) -assert("${res}" STREQUAL "hello Tobias!") - -``` - -dynamically calling a function - -``` -# three handlers -function(handler1 a) - return("handler1 ${a}") -endfunction() -function(handler2 a) - return("handler2 ${a}") -endfunction() -function(handler3 a) - return("handler3 ${a}" ) -endfunction() -# list of handlers -set(handlers handler1 handler2 handler3) -# intialize result -set(results) -# set input value -set(val 3) -foreach(handler ${handlers}) - # dynamically call handler - eval("${handler}(${val})") - ans(res) - # append result to list - list(APPEND results ${res}) -endforeach() -# check if list equals expected results -assert(EQUALS ${results} "handler1 3" "handler2 3" "handler3 3") -``` - - -# Events - -Events are often usefull when working with modules. CMake of course has no need for events generally. Some of my projects (cutil/cps) needed them however. For example the package manager cps uses them to allow hooks on package install/uninstall/load events which the packages can register. - - -## Example - - -``` -# create an event handler -function(my_event_handler arg) - message("${event_name} called: ${arg}") - return("answer1") -endfunction() - -# add an event handler -event_addhandler(irrelevant_event_name my_event_handler) -# add lambda event handler -event_addhandler(irrelevant_event_name "(arg)->return($arg)") -# anything callable can be used as an event handler (even a cmake file containing a single function) - -# emit event calls all registered event handlers in order -# and concatenates their return values -# side effects: prints irrelevent_event_name called: i am an argument -event_emit(irrelevant_event_name "i am an argument") -ans(result) - - -assert(EQUALS ${result} answer1 "i am an argument") -``` - -## Functions and Datatypes - -* `` a globally unique identifier for an event (any name you want except `on_event` ) -* `on event` a special event that gets fired on all events (mainly for debugging purposes) -* `event_addhandler( )` registers an event handler for the globally unique event identified by `` see definition for callable in [functions section](#functions) -* `event_removehandler( )` removes the specified event handler from the handler list (it is no longer invoked when event is emitted) -* `event_emit( [arg ...]) -> any[]` invokes the event identified by `` calls every handler passing along the argument list. every eventhandler's return value is concatenated and returned. It is possible to register event handlers during call of the event itself the emit routine continues as long as their are uncalled registered event handlers but does not call them twice. -* ... (functions for dynamic events, access to all available events) - -# Version Control System utilities - -Working with the version control system can be a crutch in CMake. Therefore I created helpers and convenience functions which allow simple usage. Consider the following CMake code which you would have to use to clone cutil's git repository - -``` - -find_package(Git) -if(NOT GIT_FOUND) - message(FATAL_ERROR "Git is required!") -endif() -set(cutil_base_dir "/some/path") -if(NOT IS_DIRECTORY "${cutil_base_dir}") - if(EXISTS "${cutil_base_dir}") - message(FATAL_ERROR "${cutil_base_dir} is a file") - endif() - file(MAKE_DIRECTORY "${cutil_base_dir}") -endif() -execute_process(COMMAND "${GIT_EXECUTABLE}" clone "https://github.com/toeb/cutil.git" "${cutil_base_dir}" RESULT_VARIABLE error ERROR_VARIABLE error) -if(error) - message(FATAL_ERROR "could not clone https:// .... because "${error}") -endif() -execute_process(COMMAND "${GIT_EXECUTABLE}" submodule init WORKING_DIRECTORY "${cutil_base_dir}" RESULT_VARIABLE error) -if(error) - message(FATAL_ERROR "could not init submodules because "${error}") -endif() -execute_process(COMMAND "${GIT_EXECUTABLE}" submodule update --recursive WORKING_DIRECTORY "${cutil_base_dir}" RESULT_VARIABLE error) -if(error) - message(FATAL_ERROR "could not update submodules") -endif() -``` - -Using convenience functions this distills down to - -``` -# set the current directory to ${cutil_base_dir} and creates it if it does not exist -cd(${cutil_base_dir} --create) -# automatically fails on error with error message -git(clone "http://github.com/toeb/cutil.git" .) -git(submodule init) -git(submodule update --recursive) -``` - -So alot of unecessary repeating code can be omitted. - -## Git - -### Functions - -* `git()` function for git command line client with same usage, except `git ...` -> `git(...)` (created using wrap_executable) -* `git_base_dir([]) -> ` returns the repositories base directory -* `git_dir([]) -> ` returns the repositories .git directory -* `git_read_single_file( ) -> ` reads a file from a remote repository -* `git_remote_exists() -> bool` returns true if the uri points to a valid git repository -* `git_remote_refs() -> []` returns a list of `` objects -* `` a objects containing the following fields - * `type : HEAD | ` -> the type of ref - * `name : ` -> the name of the ref - * `revision: ` -> the revision associated with the ref -* `git_repository_name() -> ` returns the name of the repository. - -## Subversion - -* `svn()` function for svn command line client with same usage, except `svn ...` -> `svn(...)` (created using wrap_exetuable) -* `svn_get_revision()` returns the revision number of the specifed `` -* `svn_info()->` returns an object containing the following fields - - path: specified relative path - - revision: revision number - - kind - - url: uri - - root: repository root - - uuid -* ... - -## Mercurial - -* `hg()` function for mercurial command line client with same usage except `hg ...` -> `hg(...)` -* ... - - - - -# Filesystem - -I have always been a bit confused when working with cmake's file functions and the logic behind paths (sometimes they are found sometimes they are not...) For ease of use I reimplemented a own path managing system which behaves very similar to powershell and bash (see [ss64.com](http://ss64.com/bash/)) and is compatible to CMake's understanding of paths. It is based around a global path stack and path qualification. All of my functions which work with paths use this system. To better show you what I mean I created the following example: - -``` -# as soon as you include `cmakepp.cmake` the current directory is set to -# "${CMAKE_SOURCE_DIR}" which is the directory from which you script file -# is called in script mode (`cmake -P`) or the directory of the root -# `CMakeLists.txt` file in configure and build steps. -pwd() # returns the current dir -ans(path) - -assert("${path}" STREQUAL "${CMAKE_SOURCE_DIR}") - - -pushd("dir1" --create) # goto ${CMAKE_SOURCE_DIR}/dir1; Create if not exists -ans(path) -assert("${path}" STREQUAL "${CMAKE_SOURCE_DIR}/dir1") - -fwrite("README.md" "This is the readme file.") # creates the file README.md in dir1 -assert(EXISTS "${CMAKE_SOURCE_DIR}/dir1/README.md") - - -pushd(dir2 --create) # goto ${CMAKE_SOURCE_DIR}/dir1/dir2 and create it if it does not exist -fwrite("README2.md" "This is another readme file") - -cd(../..) # use relative path specifiers to navigate path stack -ans(path) - -assert(${path} STREQUAL "${CMAKE_SOURCE_DIR}") # up up -> we are where we started - -popd() # path stack is popped. path before was ${CMAKE_SOURCE_DIR}/dir1 -ans(path) - -assert(${path} STREQUAL "${CMAKE_SOURCE_DIR}/dir1") - - -mkdir("dir3") -cd(dir3) -# current dir is now ${CMAKE_SOURCE_DIR}/dir1/dir3 - -# execute() uses the current pwd() as the working dir so the following -# clones the cmakepp repo into ${CMAKE_SOURCE_DIR}/dir1/dir3 -git(clone https://github.com/toeb/cmakepp.git ".") - - -# remove all files and folders -rm(.) - - -popd() # pwd is now ${CMAKE_SOURCE_DIR} again and stack is empty - -``` - - -## Functions and datatypes - -* ` ::= ` -* ` ::= ` -* `` a windows path possibly with and possibly with drive name `C:\Users\Tobi\README.md` -* `` a simple relative path '../dir2/./test.txt' -* `` a path starting with a tilde `~` which is resolved to the users home directory (under windows and posix) -* `` a fully qualified path depending on OS it only contains forward slashes and is cmake's `get_filename_component(result "${input} REAL_PATH)` returns. All symlinks are resolved. It is absolute -* ` ::= |||` -* ` ::= ` -* `path()->` qualifies a path and returns it. if path is relative (with no drive letter under windows or no initial / on unix) it will be qualified with the current directory `pwd()` -* `pwd()-> ` returns the top of the path stack. relative paths are relative to `pwd()` -* `cd( [--create]) -> ` changes the top of the path stack. returns the `` corresonding to input. if `--create` is specified the directory will be created if it does not exist. if `cd()` is navigated towards a non existing directory and `--create` is not specified it will cause a `FATAL_ERROR` -* `pushd( [--create]) -> ` works the same `cd()` except that it pushes the top of the path stack down instead of replacing it -* `popd()->` removes the top of the path stack and returns the new top path -* `dirs()-> []` returns all paths in the path stack from bottom to top -* file functions - - `fread()->` returns the contents of the specified file - - `lines()->[]` returns the contents of the specified file in a list of lines - - `download( [] [--progress])` downloads the file to target, if target is an existing directory the downloaded filename will be extracted from uri else path is treated as the target filepath - - `fappend( )->void` appends the specified content to the target file - - `fwrite( )->void` writes the content to the target file (overwriting it) - - `parent_dir()->` returns the parent directory of the specified path - - `file_timestamp()->` returns the timestamp string for the specified path yyyy-MM-ddThh:mm:ss - - `ls([])->[]` returns files and subfolders of specified path - - `mkdir()->` creates the specified dir and returns its qualified path - - `mkdirs(...)->[]` creates all of the directories specified - - `mktemp([])->` creates a temporary directory optionally you can specify where this directory is created (by default it is created in TMP_DIR) - - `mv( |[ ...] )->void` moves the specifeid path to the specified target if last argument is an existing directory all previous files will be moved there else only two arguments are allowed - - `paths([ ...])->[]` returns the qualified path for every unqualified path received as input - - `touch( [--nocreate])->` touches the specified file creating it if it does not exist. if `--nocreate` is specified the file will not be created if it does not exist. the qualified path for the specified file is returned - - `home_dir()->` returns the users home directory - - `home_path()->` returns fully qualified path relative to the user's home directory - - ... (more functions are coming whenver they are needed) - -# Shell - - -## Console Interaction - -Since I have been using cmake for what it has not been created (user interaction) I needed to enhance console output and "invent" console input. using shell magic it became possible for me to read input from the shell during cmake execution. You can see it in action in the interactive cmake shell `icmake` (start it by running cmake -P icmake.cmake) Also I was missing a way of writing to the shell without appending a linebreak - using `cmake -E echo_append` it was possibly for me to output data without ending the line. - -### Enhanced `message` function - -When working with recursive calls and complex build processes it is sometimes useful to allow output to be indented which helps the user understand the output more easily. - -Therfore I extended the `message` function to allow some extra flags which control indentation on the console. The indentation functionality can also be controlled using the `message_indent*` functions. Take the following example: - -*Example* - -``` -function(add_some_lib) - message(PUSH_AFTER "adding somelib") - ... - message(POP_AFTER "done") -endfunction() -function(add_my_target) - message(PUSH_AFTER "adding my target") - ... - message("gathering sources") - add_some_lib() - ... - message(POP_AFTER "complete") -endfunction() - -message("condiguring") -add_some_lib() -add_my_target() -message("finished") -``` - -*Output*: -``` -adding somelib -adding my target - gathering sources - adding somelib - done - complete -finished - -``` - -### Functions - -* `read_line()->` prompts the user to input text. waiting for a line break. the result is a string containing the line read from console -* `echo_append([args ...])` appends the specifeid arguments to stdout without a new line at the end -* `echo([args ...])` appends the specified arguments to stdout and adds a new line -* `message` -* `message( )` enhanced message function (with indentation) - - `PUSH` -* `print(str)` prints the specified string to console (default is stderr...) using `_message` -* `message_indent_level():` returns the level of indentation -* `message_indent_get():<>` returns the indentation string `string_repeat(" " ${n})` (two spaces times the indentation level) -* `message_indent_push(>):` pushes the specified level on to the indentation stack making it the current level. if the number is preceded by `+` or `-` the value is relative to the current indentation level. -* `message_indent_pop():` removes the last level from the stack and returns the new current level -* `message_indent()` writes the string to the console indenting it - - - -## Aliases - -Since I like to provide command line tools based on cmake (using cmake as a cross plattform shell in some sense) I also needed the ability to create aliases in a platform independent way. Even though I have a couple of limitations I have found a good posibillity to do what I want. See the following example of how to create a cross platform way to dowload a file: - -``` -fwrite("datetimescript.cmake" " -include(\${cmakepp_base_dir}/cmakepp.cmake) -datetime() -ans(dt) -json_print(${dt}) -") -path("datetimescript.cmake") -ans(fullpath) -alias_create("my_datetime" "cmake -P \"${fullpath}\"") - -``` - -After executing the above code the current shell will have access to the my_datetime alias and the following call will be possible without any reference to cmake - in this case the cmake command is called of course but `create_alias` can be used to create a alias for any type of executable as bash and windows commandprompts do not differ too much in this respect - -``` -shell> my_datetime -{ - "yyyy":"2014", - "MM":"11", - "dd":"27", - "hh":"22", - "mm":"51", - "ss":"32", - "ms":"0" -} -``` - -### Functions and Datatypes - -* `alias_create( )` - under windows this registers a directory in the users PATH variable. In this directory batch files are created. Under Unix the .bashrc is edited and a alias is inserted. - - Not implemented yet: - + `alias_exists` - + `alias_remove` - + `alias_list` - - - - - -# CMake Command - -Like the version control system I also wrappend cmake itself into an easy to use function called `cmake(...)` this allows me to start subinstances of cmake - -# CMake Tooling - -To create tools for CMake (wherever something cannot be done with cmake) I created a very simple function called `compile_tool( )` - -This function does the following tasks - -* creates a cmake project with CMakeLists file and a single executable target the target has a single source file (whatever you put in ``) -* compiles this tool (the tool may only use standard headers currently) -* caches the compiled tool so that it is not recompiled unless src changes -* creates a wrapper function in cmake called `` which evaluates the cmake code that the command outputs -* arguments passed to wrapper `` are passed along as command line args for the main method. - -*note*: this will change in the future as it is a very naive implementation. - - -## Example - -Because cmake does not support getting the current time in milliseconds since the epoch the need arose for me to compile custom code. This code is the example for using the `compile_tool(..)` function. - -``` -## returns the number of milliseconds since epoch -function(millis) - # initializer function pattern - because compile_tool - # redefines the millis function this code is only executed once - compile_tool( - # first argument: the name of the tool - # and the function name to be defined - millis - # second argument: the source code to compile - # with default compiler/generator under you system - " - #include - #include - int main(int argc, const char ** argv){ - // use chrono to get the current time in milliseconds - auto now = std::chrono::system_clock::now(); - auto duration = now.time_since_epoch(); - auto millis = std::chrono::duration_cast(duration).count(); - // return cmake code which is to be evaluated by command wrapper - // set_ans will let the eval function return the specified value - std::cout<< \"set_ans(\" << millis << \")\"; - return 0; - } - " - ) - ## compile_tool has either failed or succesfully defined the function - ## wrapper called millis - millis(${ARGN}) - return_ans() -endfunction() - -# now you can use millis -millis() -ans(ms) -message("time since epoch in milliseconds: ${ms}") -``` - - -# User Data - -User Data is usefull for reading and writing configuration per user. It is available for all cmake execution and can be undestood as an extra variable scope. It however allows maps which help structure data more clearly. User Data is stored in the users home directory (see `home_dir`) where a folder called `.cmakepp` inside are files in a quickmap format which can be edited in an editor of choice besides being managed by the following functions. User Data is always read and persisted directly (which is slower but makes the system more consistent) - -## Functions and Datatypes - -* `` a string -* `user_data_get(> [>|"."|""]):` returns the user data for the specified identifier, if a navigation expression is specified the userdata map will be navigated to the specified map path and the data is returned (or null if the data does not exist). -* `user_data_set(> <>|"."|""|> []):` sets the user data identified by id and navigated to by navigation -* `user_data_dir():` returns the path where the userdata is stored: `$HOME_DIR/.cmakepp` -* `user_data_ids():` returns a set of identifiers where user data was stored -* `user_data_clear(<"--all"^>>):` if `--all` is specified all user data is removed. (use with caution) else only the user data identified by `` is removed -* `user_data_read(>):` deserializes the user data identified by id and returns it (`user_data_get` and `user_data_set` are based on this function) -* `user_data_write(> [ ...>]):` serializes and persists the specified data and associates it with `` -* `user_data_path(> ):` returns the filename under which the user data identified by `` is located - -## Example - -``` - -## store user data during cmake script execution/configuration/generation steps -## this call creates and stores data in the users home directory/.cmakepp -user_data_set(myuserdata configoptions.configvalue "my value" 34) - - -## any other file executed afterwards -user_data_get(myuserdata) -ans(res) - -json_print(${res}) - -## outputs the following -# { -# configoptions:{ -# configvalue:["my value",34] -# } -# } - -``` - -# Process Management - -This section how to manage processes and external programs. Besides replacements for cmake's `execute_process` function this section defines a control system for parallel processes controlled from any cmake. - -## Common Definitions - -The following definitions are common to the following subsections. - -* `` a path or filename to an executable programm. -* ` ::= { , <>, > }` - - -## Wrapping and Executing External Programms - -Using external applications is more complex than necessary in cmake in my opinion. I tried to make it as easy as possible. Using the convenience of the [filesystem functions](#filesystem) and maps wrapping an external programm and using it as well as pure execution is now very simple as you can see in the following example for git: - -### Examples -This is all the code you need to create a function which wraps the git executable. It uses the [initializer function pattern](#initializer_function). - -``` -function(git) - # initializer block (will only be executed once) - find_package(Git) - if(NOT GIT_FOUND) - message(FATAL_ERROR "missing git") - endif() - # function redefinition inside wrap_executable - wrap_executable(git "${GIT_EXECUTABLE}") - # delegate to redefinition and return value - git(${ARGN}) - return_ans() -endfunction() -``` - -Another example showing usage of the `execute()` function: - -``` -find_package(Hg) -set(cmdline --version) -execute({path:$HG_EXECUTABLE, args: $cmdline} --result) -ans(res) -map_get(${res} result) -ans(error) -map_get(${res} output) -ans(stdout) -assert(NOT error) # error code is 0 -assert("${stdout}" MATCHES "mercurial") # output contains mercurial -json_print(${res}) # outputs input and output of process - -``` - -### Functions and Datatypes - -* `execute( [--result|--return-code]) -> ||` executes the process described by `` and by default fails fatally if return code is not 0. if `--result` flag is specified `` is returned and if `` is specified the command's return code is returned. (the second two options will not cause a fatal error) - * example: `execute("{path:'', args:['a','b']}")` -* `wrap_executable( )` takes the executable/command and wraps it inside a function called `` it has the same signature as `execute(...)` -* `` a string which can be converted to a `` object using the `process_start_info()` function. -* `` a map/object containing the following fields - - `command` command name / path of executable *required* - - `args` command line arguments to pass along to command, use `string_semicolon_encode` if you want to have an argument with semicolons *optional* - - `timeout:` *optional* number of seconds to wait before failing - - `cwd:` *optional* by default it is whatever `pwd()` currently returns -* `` contains all the fields of `` and additionaly - - `output:` the output of the command execution. (merged error and stdout streams) - - `result:` the return code of the execution. 0 normally indicates success. -* `process_start_info():` creates a valid `` object from the input argument. If the input is invalid the function fails fatally. - -## Parallel Processes - -When working with large applications in cmake it can become necessary to work in parallel processes. Since all cmake target systems support multitasking from the command line it is possible to implement cmake functions to control it. I implemented a 'platform independent' (platform on which either powershell or bash is available) control mechanism for starting, killing, querying and waiting for processes. The lowlevel functions are platform specific the others are based on the abstraction layer that the provide. - -### Examples - -This example starts a script into three separate cmake processes. The program ends when all scripts are done executing. -``` -# define a script which counts to 10 and then -# note that a fresh process means that cmake has not loaded cmakepp -set(script " -foreach(i RANGE 0 10) - message(\${i}) - execute_process(COMMAND \${CMAKE_COMMAND} -E sleep 1) -endforeach() -message(end) -") - -# start each script - fork_script returns without waiting for the process to finish. -# a handle to the created process is returned. -process_start_script("${script}") -ans(handle1) -process_start_script("${script}") -ans(handle2) -process_start_script("${script}") -ans(handle3) - -# wait for every process to finish. returns the handles in order in which the process finishes -process_wait_all(${handle1} ${handle2} ${handle3}) -ans(res) - -## print the process handles in order of completion -json_print(${res}) - -``` - -This example shows a more usefull case: Downloading multiple 'large' files parallely to save time - -``` - - ## define a function which downloads - ## all urls specified to the current dir - ## returns the path for every downloaded files - function(download_files_parallel) - ## get current working dir - pwd() - ans(target_dir) - - ## process start loop - ## starts a new process for every url to download - set(handles) - foreach(url ${ARGN}) - ## start download by creating a cmake script - process_start_script(" - include(${cmakepp_base_dir}/cmakepp.cmake) # include cmakepp - download(\"${url}\" \"${target_dir}\") - ans(result_path) - message(STATUS ${target_dir}/\${result_path}) - ") - ans(handle) - ## store process handle - list(APPEND handles ${handle}) - endforeach() - - ## wait for all downloads to finish - process_wait_all(${handles}) - - set(result_paths) - foreach(handle ${handles}) - ## get process stdout - process_stdout(${handle}) - ans(output) - - ## remove '-- ' from beginning of output which is - ## automatically prependend by message(STATUS) - string(SUBSTRING "${output}" 3 -1 output) - - ## store returned file path - list(APPEND result_paths ${output}) - - endforeach() - - ## return file paths of downloaded files - return_ref(result_paths) - endfunction() - - - ## create and goto ./download_dir - cd("download_dir" --create) - - ## start downloading files in parallel by calling previously defined function - download_files_parallel( - http://www.cmake.org/files/v3.0/cmake-3.0.2.tar.gz - http://www.cmake.org/files/v2.8/cmake-2.8.12.2.tar.gz - ) - ans(paths) - - - ## assert that every the files were downloaded - foreach(path ${paths}) - assert(EXISTS "${path}") - endforeach() - - -``` - - -### Functions and Datatypes -* datatypes - * ` ::= { state: , pid: }` process handle is a runtime unique map which is used to address a process. The process handle may contain more properties than specified - only the specified ones are available on all systems - these properties contain values which are implementation specific. - * ` ::= { }` a map containing verbose information on a proccess. only the specified fields are available on all platforms. More are available depending on the OS you use. You should not try to use these without examining their origin / validity. - * ` ::= "running"|"terminated"|"unknown"` - * ` ::= ` a unspecified systemwide unique string which identifies a process (normally a integer) -* platform specific low level functions - * `process_start():` platfrom specific function which starts a process and returns a process handle - * `process_kill()` platform specific function which stops a process. - * `process_list():` platform specific function which returns a process handle for all running processes on OS. - * `process_info():` platform specific function which returns a verbose process info - * `process_isrunning():` returns true iff process is running. -* `process_timeout(>):` starts a process which times out in `` seconds. -* `process_wait( [--timeout ]):` waits for the specified process to finish or the specified timeout to run out. returns null if timeout runs out before process finishes. -* `process_wait_all( <[--timeout ] [--quietly]):` waits for all specified process handles and returns them in the order that they completed. If the `--timeout ` value is specified the function returns as soon as the timeout is reached returning only the process finished up to that point. The function normally prints status messages which can be supressed via the `--quietly` flag. -* `process_wait_any( >> ):` waits for any of the specified processes to finish, returning the handle of the first one to finished. If `--timeout ` is specified the function will return `null` after `n` seconds if no process completed up to that point in time. You can specify `--quietly` if you want to suppress the status messages. -* `process_stdout():` returns all data written to standard output stream of the process specified up to the current point in time -* `process_stderr():` return all data written to standard error stream of the process specified up to the current point in time -* `process_return_code():` returns nothing or the process return code if the process is finished -* `process_start_script():` starts a separate cmake process which runs the specified cmake code. - -### Inter Process Communication - -To communicate with you processes you can use any of the following well known mechanisms - -* Environment Variables - - the started processes have access to you current Environment. So when you call `set(ENV{VAR} value)` before starting a process that process will have read access to the variable `$ENV{VAR}` -* Command Line Arguments - - all variables passed to `start_process` will be passed allong - - Command Line Variables are sometimes problematic as they must be escaped correctly and this does not always happen as expected. So you might want to choose another mechanism to transmit complex data to your process - - Command Line Variables are limited by their string length depending on you host os. -* Files - - Files are the easiest and safest way to communicate large amounts of data to another process. If you can try to use file communication -* stderr, stdout - - The output of a process started with `start_process` becomes available to you when the process ends at latest, You can choose to poll stdout and take data as soon as it is written to the output streams -* return code - - the returns code tells you how you process finished and is often enough result information for a process you start - -### Caveats - -* process starting is slow - it can take seconds (it takes 900ms on my machine). The task needs to be a very large one for it to compensate the overhead. -* parallel processes use platform specific functions - It might cause problems on less well tested OSs and some may not be supported. (currently only platforms with bash or powershell are supported ie Windows and Linux) - - - -# Uniform Resource Identifiers (URIs) - -Uniform Resource Identifiers are often used for more than just internet addresses. They are able to identify any type of resource and are truly cross platform. Even something as simple as parsing a path can take on complex forms in edge cases. My motivation for writing an URI parser was that I needed a sure way to identify a path in a command line call. - -My work is based arround [RFC2396](https://www.ietf.org/rfc/rfc2396.txt) by Berners Lee et al. The standard is enhanced by allowing delimited URIs and Windows Paths as URIs. You can always turn this behaviour off however and use flags to use the pure standard. - -URI parsing with cmake is not something you should do thousands of times because alot of regex calls go into generating an uri object. - -## Example - -*Parse an URI and print it to the Console* -``` -uri("https://www.google.de/u/0/mail/?arg1=123&arg2=arg4#readmails some other data") -ans(res) -json_print(${res}) -``` - -*output* -``` -{ - "input":"https://www.google.de/u/0/mail/?arg1=123&arg2=arg4#readmails some other data", - "uri":"https://www.google.de/u/0/mail/?arg1=123&arg2=arg4#readmails", - "rest":" some other data", - "delimiters":null, - "scheme":"https", - "scheme_specific_part":"//www.google.de/u/0/mail/?arg1=123&arg2=arg4#readmails", - "net_path":"www.google.de/u/0/mail/", - "authority":"www.google.de", - "path":"/u/0/mail/", - "query":"arg1=123&arg2=arg4", - "fragment":"readmails", - "user_info":null, - "user_name":null, - "password":null, - "host_port":"www.google.de", - "host":"www.google.de", - "labels":[ - "www", - "google", - "de" - ], - "top_label":"de", - "domain":"google.de", - "ip":null, - "port":null, - "trailing_slash":false, - "last_segment":"mail", - "segments":[ - "u", - 0, - "mail" - ], - "encoded_segments":[ - "u", - 0, - "mail" - ], - "file":"mail", - "extension":null, - "file_name":"mail" -} -``` - -*Absolute Windows Path* - -``` -# output for C:\windows\path -{ - "input":"C:\\windows\\path", - "uri":"file:///C:/windows/path", - "rest":null, - "delimiters":null, - "scheme":"file", - "scheme_specific_part":"///C:/windows/path", - "net_path":"/C:/windows/path", - "authority":null, - "path":"/C:/windows/path", - "query":null, - "fragment":null, - "user_info":null, - "user_name":null, - "password":null, - "host_port":null, - "host":null, - "labels":null, - "top_label":null, - "domain":null, - "ip":null, - "port":null, - "trailing_slash":false, - "last_segment":"path", - "segments":[ - "C:", - "windows", - "path" - ], - "encoded_segments":[ - "C:", - "windows", - "path" - ], - "file":"path", - "extension":null, - "file_name":"path" -} -``` - - -*Perverted Example* -``` -uri("'scheme1+http://user:password@102.13.44.22:23%54/C:\\Program Files(x86)/dir number 1\\file.text.txt?asd=23#asd'") -ans(res) -json_print(${res}) -``` -*output* -``` -{ - "input":"'scheme1+http://user:password@102.13.44.32:234/C:\\Progr%61m Files(x86)/dir number 1\\file.text.txt?asd=23#asd'", - "uri":"scheme1+http://user:password@102.13.44.32:234/C:/Progr%61m%20Files(x86)/dir%20number%201/file.text.txt?asd=23#asd", - "rest":null, - "delimiters":null, - "scheme":"scheme1+http", - "scheme_specific_part":"//user:password@102.13.44.32:234/C:/Progr%61m%20Files(x86)/dir%20number%201/file.text.txt?asd=23#asd", - "net_path":"user:password@102.13.44.32:234/C:/Progr%61m%20Files(x86)/dir%20number%201/file.text.txt", - "authority":"user:password@102.13.44.32:234", - "path":"/C:/Progr%61m%20Files(x86)/dir%20number%201/file.text.txt", - "query":"asd=23", - "fragment":"asd", - "user_info":"user:password", - "user_name":"user", - "password":"password", - "host_port":"102.13.44.32:234", - "host":"102.13.44.32", - "labels":null, - "top_label":null, - "domain":null, - "ip":"102.13.44.32", - "port":234, - "trailing_slash":false, - "last_segment":"file.text.txt", - "segments":[ - "C:", - "Program Files(x86)", - "dir number 1", - "file.text.txt" - ], - "encoded_segments":[ - "C:", - "Progr%61m%20Files(x86)", - "dir%20number%201", - "file.text.txt" - ], - "file":"file.text.txt", - "extension":"txt", - "file_name":"file.text" -} -``` - -## DataTypes and Functions - -* ` ::= ` - * `uri:` all of the uri as specified - * `scheme:` the scheme part of the uri without the colon e.g. `https` from `https://github.com` - * `scheme_specific_part` the part of the uri that comes after the scheme and its colon e.g. `//github.com` from the previous example - * `autority:` the domain, host,port and user info part of the domain - * `path:` the hierarchical part of the uri - * `query:` the query part of the uri - * `fragment:` the fragment part of the uri -* ` ::= |` a uri or a string which can be converted into a valid uri -* `uri():` -* `uri_parse(> ):` - * `<--escape-whitespace>` - * `<--file>` - * `<--notnull>` - * `<--delimited>` -* `uri_to_path():` -* - - -## Caveats - -* Parsing is always a performance problem in CMake therfore parsing URIs is also a performance problem don't got parsing thousands of uris. I Tried to parse 100 URIs on my MBP 2011 and it took 6716 ms so 67ms to parse a single uri -* Non standard behaviour can always ensue when working with file paths and spaces and windows. However this is the closest I came to having a general solution - -## Future Work - -* allow more options for parsing -* option for quick parse or slow parse - - -# HTTP Client - -`CMake` has a built in `cUrl` module which it exposes over its `file` function - more precise: `file(DOWNLOAD)` and `file(UPLOAD)`. These functions actually perform a `GET` resp. `PUT` request on the designated uri. I used these capabilities to create a `http_get` and `http_put` function which works like one might expect a http client to work. - -*Example* -``` -``` - -## Functions and Datatypes - -* `http_post()-> ` -* `http_get()-> ` -* - -# String Functions - -# List and Set Functions - -CMake's programming model is a bit ambigous but also very simple. Every variable can be interpreted as a list and a string. This duality makes everything a little complex because you can never know what which of both is meant. However if you tell the client of you CMake functions what you expect you start to programm by convention which is very usefull in a very simple dynamic language like CMake Script. - -## Datatypes and Functions - -* ` := ` a variable in cmake which semicolon separated strings. -* ` := ():` a function which takes a single arg and returns a truth value -* ` :: ` the name of the variable that contains a list -* ` := ` a set is a list which contains only unique elements. You can create a set by using CMake's `list(REMOVE_DUPLICATES thelist)` function. -* `index_range( ):` returns a list of indices which includes `lo` but excludes `hi`. - - `index_range(3 5)` -> `3;4` -* `list_all( ):` returns true iff predicate evaluates to true for all elements of the list -* `list_any( ):` returns true iff predicate evaluates to true for for at least one element of the list. -* `list_at( >):` returns all elements of the list which specified by their indices. Repetions are allowed. - - `list_at(thelist 1 3 0 0)` -> `b;d;a;a` if the list contains the alphabet -* `list_combinations():` returns all possible combinations of all specified lists - - `list_combinations(bin bin bin)` -> `000;001;010;011;100;101;110;111` if `bin = 0;1` -* `list_contains( ):` returns true if list contains all args specified -* `list_count( ):` returns the number of elements for which the specified predicate evalautes to true -* `list_difference( ):` -* `list_equal(> >):` -* `list_erase( ):` removes the specified range from the list -* `list_erase_slice( ):` removes the specified range from the list and returns the removed elements -* `list_except(> >):` returns the elements of lhs which are not in rhs -* `list_extract( ):` removes the first n elements of the list and returns the rest of the list. -* `list_extract_any_flag` -* `list_erase_slice( > > >):` replaces the specified range of the list with the passed varargs. returns the elements which were removed - - -## Caveats - -* some list functions wrap default cmake behaviour. That means that they are slower. So in some cases where you are doing alot of function calling you should use the default cmake functions to make everything faster. - - -### Range based List access - -Ranges are an awesome way of accessing lists. Take for example the following task: `return the last element, the 3rd element and elements 8 to 6` using cmake this can become complicated having to check list lengths and compiling the list of indices needed. However it is easier if you write `list_range_get(mylist $,2,8:6:-1)` - - -#### Functions and Datatypes - -* `list_range_get()` -* `list_range_indices()` -* `list_range_partial_write()` -* `list_range_remove()` -* `list_range_replace()` -* `list_range_set()` -* `list_range_try_get()` - -# Package Management - -Package management depends on package search and retrieval. The other way around there are no dependencies. This clean cut is and will stay important - -## The Project Lifecycle - - -* `project_new` a project is created which does not know anything -* `project_load` project configuration is loaded into `project_handle.configuration` - - qualfied paths are set in `project_handle` - + `content_dir` were the projects content is - + `dependency_dir` were the dependencies of the project are installed to - + `config_dir` were configuration files for the project are written - - installed package load in arbitrary order except the project which is loaded last - + event `project_on_package_load( )` is emitted - * `cmakepp_project_on_package_load` is called - * `package_descriptor.cmakepp.export :` all files specifed by globbing expression are loaded in the order specified. - * `package_descriptor.cmakepp.hooks.on_load: )` is emitted - - event `project_on_packages_loaded( )` is emitted - - event `project_on_load()` is emitted -* `project_install( [--reference]) ->` - - `package content and package handle is pulled and pushed into managed package source which is based in dependency_dir` - - `package_descriptor.cmakepp.hooks.on_install( )` hook is invoked if it exists - - event `project_on_package_load( )` is emitted - * `cmakepp_project_on_package_load` is called - * `package_descriptor.cmakepp.export :` all files specifed by globbing expression are loaded in the order specified. - * `package_descriptor.cmakepp.hooks.on_load: Package Search and Retrieval - -## Motivation - -A best practice for retrieving and using third party libraries with a platform independent build system (for c++) is currently not available. So I have decided to throw my hat in the ring by adding package control to cmake. I compete with other great solutions in this area: - -* [biicode]() a file based dependency management system depending on a central webservice. -* [hunter]() a decentralized package manager with a central repository for `hunter gates` -* [cpm]() a git, subversion and hg based package manager also indexes packages in a github repository - -I want to be able to use decentralized package sources (ie not a centralized server through which all requests go) and be easily extinsible to incoporate other package services with minimal overhead. - -I want package search and retrieval to be a very simple process so it can be applied generally. (no specialization on C++, no callbacks, installation, etc - these subjects are important and are adressed but not in respect to package search and retrieval) - -Using package search and retrieval as a base I will extend it by [dependency management](#) and [cpp project generation](#). - -## Implementation - -I chose a very simple interface to handle packages *note: these functions exist for every `package source` (not globally) - except pull which is only implemented by writable data sources*: - -* `` is an abstract term. It describes a set of files and meta information (``) which is identified by a `` -* `` meta information on a package. No constraint on the data is made. There are however some properties which have special meaning and are listed here - * `id: ` the name of the `package` it should be uniqueish. At least in its usage context it should be unique. - * `version: ` the version of the package - * `...` other properties do not pertain to package search and retrieval but rather to project- and dependency-management these are described elsewhere. -* `` is a `` of an existing uniquely identified `` this uri identifies a ``'s contents and `package descriptor` the content and meta information of the package SHOULD BE immutable. This constraint is needed to allow for dependency management and guaranteeing a package's reliability. Every ``package source`` will tell you what kind of guarantee it gives your. -* `` a package handle is an object containing information on a package and has a unique key called `uri`. The data varies for a package handle depending on the `package source` it stems from. Some sources might add more meta information than others. - - properties - * `uri :` required. uniquely identifies this package - * `package_descriptor: ` required after resolve (may be null or generated.) - * `content_dir:` required after successful pull - * `...` -* `query(<~uri> [--package-handle]) -> ` takes a uri query and returns a list of unique unchanging `` which will be valid now and generally in the future. It is important to note the unchanging aspect as once a package is uniquely identified the user expects it only to change when he/she wants it to. - * `--package-handle` will return a `` instead of a `` - * `--refresh` will cause an update of any cache being used. `package source`s which do not cache will silently ignore this flag. -* `resolve(<~uri> [--refresh]) -> ` takes a uri and returns a `` if the uri uniquely identifies a package. If the uri specified is not unique null is returned. - * `--refresh` will cause an update of any cache being used. `package source`s which do not cache will silently ignore this flag. -* `pull(<~uri> [--refresh] [--reference]) -> ` takes a uri which is resolved to a package. The content of the package is then loaded into the specified target dir (relative to current `pwd`) The returned package handle will contain a property called `content_dir` which will normally but not necessarily be the same to `target_dir` - - `--refresh` will cause an update of any cache being used - - `--reference` will set the `content_dir` of the package handle to a local path which already contains the content associated with the `package uri` if the `package source` does not support the reference flag it ifnore the `--reference` flag -* `push(<~package handle> ...) -> ` the implemenation of this function is not necessary and not available for all `package source`s - it allows upload of a package to a `package source`. After the successfull upload the `` is returned. - - `...` arguments dependening on the `package source` being used. - -The `package source`s described hitherto all have a constructor function which returns a `package source` object. The `pull`/`push`/`resolve`/`query` implementations have a longer function name and SHOULD NOT be used directly but by calling them on the `package source` object using `call(...)` or `assign(...)`. - -If your are just interested in pulling packages from a remote to a target directory you should use the [default package methods](#packages_default_methods): `pull_package`, `resolve_package`, `query_package` which work globally and need no special `package source` object. - -*Examples* - -``` -## create a github package source and use it to find all local repositories of a github user and print them to the console -set(user "toeb") -assign(source = github_package_source()) -assign(package_uris = source.query("${user}")) -message("all packages for ${user}") -foreach(package_uri ${package_uris}) - message(" ${package_uri}") -endforeach() -``` - -### Default Package Source nad Default Package Functions - -The default package source combines access to github, bitbucket,webarchives, git, svn, hg, local archives and local dirs in a single package source. - -It can be accessed conveniently by these global functions - -* `default_package_source() -> ` -* `query_package(<~uri>):` -* `resolve_package(<~uri>):` -* `pull_package(<~uri> ):` - -*Examples* - -``` - -## pull a github package to current user's home dir from github -pull_package("toeb/cmakepp" "~/current_cmakepp") - -## pull a bitbucket package to pwd -pull_package("eigen/eigen") - -## pull a package which exists in both bitbucket and github under the same user/name from github -pull_package("github:toeb/test_repo") - -## find all packages from user toeb in bitbucket and github - -assign(package_uris = query_package(toeb)) -foreach(package_uri ${package_uris}) - message(" ${package_uri}") -endforeach() - -``` - -### Package Sources - -A `package source` is a set of methods and possibly some implementation specific properties. The interface for a `package source` was already described and consists of the methods. -* `query` -* `resolve` -* `pull` -* `push` *optional* - -In the following sections the package source implementations are briefly discussed. - -#### github package source - -A package source which uses the github api to parse remote source packages. The idea is to use only the `/` string to identify a source package. - -* `query uri format` a combination of `//`. specifying only the user returns all its repositories specifying user and repository will return the current default repository. specifying a branch will also check the branch -* `package uri format` a uri of the following format `github://` -* Functions - - `github_package_source() -> ` returns a github package source object which the following implementations. - - `query: package_source_query_github(...)->...` - - `resolve: package_source_resolve_github(...)-> ` package handle contains a property called `repo_descriptor` which contains github specific data to the repository - - `pull: package_source_pull_github(...)->...` - - -#### bitbucket package source - -A package source which uses the bitbucket api to parse remote source packages. - -* `query uri format` a combination of `//`. specifying only the user returns all its repositories specifying user and repository will return the current default repository. specifying a branch will also check the branch -* `package uri format` a uri of the following format `bitbucket://` -* Functions - - `bitbucket_package_source() -> ` returns a bitbucket package source object which contains the following methods. - - `package_source_query_bitbucket(...)->...` - - `package_source_resolve_bitbucket(...)-> ` package handle contains a property called `repo_descriptor` which contains bitbucket specific data to the repository - - `package_source_pull_bitbucket(...)->...` - - -#### path package source - -* `query uri format` - takes any local `` (relative or absolute) or local path uri (`file://...`) that is points to an existing directory. (expects a `package descriptor file` in the local directory. -* `package uri format` - a file schemed uri with no query which contains the absolute path of the package (no relative paths allowed in *unique* resource identifier) -* Functions - - `path_package_source() -> ` returns a path package source object which ontains the methods described above - - `package_source_query_path(...)->...` - - `package_source_resolve_path(...)->...` - - `package_source_pull_path(...)->...` - - `package_source_push_path(...)->...` - -*Examples* - -* valid query uris - - `../pkg` relative path - - `C:\path\to\package` absolute windows path - - `pkg2` relative path - - `/home/path/pkg3` absolute posix path - - `~/pkgX` absolute home path - - `file:///C:/users/tobi/packages/pkg1` valid file uri - - `file://localhost/C:/users/tobi/packages/pkg1` valid file uri -* valid package uris - - `file:///usr/local/pkg1` - - `file://localhost/usr/local/pkg1` -* valid local package dir - - contains `package.cmake` - a json file describing the package meta data - -#### archive package source - -*Note: Currently only application/x-gzip files are supported - the support for other formats is automatically extended when decompress/compress functions support new formats* - -* `query uri format` - takes any local `` (relative or absolute) or local path uri (`file://...`) that points to an existing archive file (see `compress`/`decompress` functions) -* `package uri format` - a file schemed uri which contains the absolute path to a readable archive file. -* Functions - - `archive_package_source() -> ` - - `package_source_query_archive(...)->...` - - `package_source_resolve_archive(...)->...` - - `package_source_pull_archive(...)->...` - - `package_source_push_archive(...)->...` - -*Examples* - -* valid query uris - - `../pkg.tar.gz` relative path - - `C:\path\to\package.gz` absolute windows path to existing tgz file - - `pkg3.7z` (does not work until decompress works with 7z files however correct nonetheless) - - `~/pkg4.gz` home path - - `file:///path/to/tar/file.gz` -* valid package uris - - `file:///user/local/pkg1.tar.gz` - - `file://localhost/usr/local/pkg1.tar.gz` - -#### web archive package source - -*Note: same as local archive* - -* `query uri format` - takes any uri which points to downloadable archive file. (including query) (normally the scheme would be `http` or `https` however only the protocol needs to be http as this package source sends a `HTTP GET` request to the specified uri.) See `http_get` for more information on how to set up security tokens etc. -* `package uri format` - same as `query uri format` -* Functions - - `webarchive_package_source() -> ` - - `package_source_query_webarchive(...)->...` - - `package_source_resolve_webarchive(...)->...` tries to read the `package descriptor` inside the archive. If that fails tries to parse the filename as a package descriptor. - - `package_source_pull_webarchive(...)->...` - - NOT IMPLEMENTED YET `package_source_push_webarchive(<~package handle> >)->...` uses `http_put` to upload a package to the specified uri - -*Examples* - -* valid query uris - - `http://downloads.sourceforge.net/project/jsoncpp/jsoncpp/0.5.0/jsoncpp-src-0.5.0.tar.gz?r=http%3A%2F%2Fsourceforge.net%2Fprojects%2Fjsoncpp%2F&ts=1422460575&use_mirror=switch` - - `http://github.com/lloyd/yajl/tarball/2.1.0` - -#### git package source - -Uses the source code management sytem `git` to access a package. A git repository is interpreted as a package with refs (tags/branches/hashes) being interpreted as different version. - -* `query uri format` - takes any uri which git can use (`https`, `ssh`, `git`, `user@host:repo.git`) internally `git ls-remote` is used to check if the uri points to a valid repository. You can specify a ref, branch or tag by appending a query to the uri e.g. `?tag=v0.0.1` -* `package uri format` - same as `query uri format` but with the additional scheme `gitscm` added -* Functions - - `git_package_source()` - - `package_source_query_hg(<~uri>) -> ` - - `package_source_resolve_hg(<~uri>) -> ` - - `package_source_pull_hg(<~uri>) -> ` - -#### mercurial package source - -Uses the source code management system `mercurial` to access packages. - -* `query uri format` - any uri which the `hg` executable can use -* `package uri format` - same as `query uri format` but with the additional scheme `hgscm` added. The query only contains `?=` if a specific revision is targeted -* Functions - - `hg_package_source()-> ` - - `package_source_query_hg(<~uri>) -> ` - - `package_source_resolve_hg(<~uri>) -> ` - - `package_source_pull_hg(<~uri>) -> ` - -#### subversion package source - -uses the source code management system `subversion` to access packages - -* Functions - - `svn_package_source()-> ` - - `package_source_query_svn(<~uri>) -> ` - - `package_source_resolve_svn(<~uri>) -> ` - - `package_source_pull_svn(<~uri>) -> ` - - -#### composite package source - -A composite package source manages a list of sub data sources and uses a rating algorithm to select the correct source. If one of the schemes of an uri matches a `package sources`'s `source_name` it is selected. Else the `package source`'s `rate_uri()->` method is called which returns a value from `0` to `999` where `0` means package source cannot handle the uri and `999` means package source is the only one which can handle the uri. The sources are ordered by the rating and queried in order. - -* `query uri format` -* `` contains the property `rating` which contains the rating of the uri and `package_source` which contains the package source which handles the `uri` -* - -#### cached package source - -The cache package source caches the package query and resolve requests so that accessing them is quick. - -* Functions - - `cache_package_source(>) -> ` - - `package_source_query_cached(...)->...` - - `package_source_resolve_cached(...)->...` - - `package_source_pull_cached(...)->...` - - -#### directory package source - -The directory package source has a `source_name` and a `directory` associated with it. It treets every `subdirectory` as a possible package and allows query, resolve and pull operations on them. The `package descriptor` is sought for in the `subdirectory`s `package descriptor file` The content is copied as is described by the `path package source` - -* Functions - - ` ::= { source_name:, directory:, query:, resolve:, pull: }` - - `directory_package_source( >) -> ` - - - -#### managed package source - -A managed package source has a `source_name` and a `directory` which it manages. The managed package source should be considered as a black box and should only be accessed via its (push, pull, query and resolve) methods. - -* `query uri format` -* `package uri format` `:` -* `` the package handle contains extra fields - - - - -## Datatypes and Functions - -# Shell Functions - -# Expression Syntax - -# Implementation Notes - - -## Initializer function - -If you want to execute code only once for a function (e.g. create a datastructure before executing the function or finding a package) you can use the Initializer Pattern. Which redefines the function itside itself after executing one time code. - -``` -function(initalizing_function) - # initialization code - function(initializing_function) - # actual function code - endfunction() - initializing_function(${ARGN}) - return_ans() # forwards the returned value -endfunction() -``` - -*Example* -``` -function(global_counter) - ref_set(global_counter_ref 0) - function(global_counter) - ref_get(global_counter_ref) - ans(count) - math(EXPR count "${count} + 1") - ref_set(global_counter_ref ${count}) - return(${count}) - endfunction() -endfunction() -``` - - -## Passing By Ref - - - -Passing a variable to a function can be done by value and or by reference. -``` -function(byval var) - message("${var}") -endfunction() - -function(byref ref) - message("${${ref}}") -endfunction() - -set(some_val 3) - -byval(${some_val}) # prints 3 -byref(some_val) # prints 3 - -``` - -Passing 'by ref' is possible because a function inherits its parent scope. The problem with passing by ref is the following: - -``` -function(byref ref) - set(val 1) - message("${${ref}} ${val}") -endfunction() - -set(val 2) -byref(val) #expected to print "2 1" but actually prints "2 2" -``` - -The `val` of the function's scope overwrites the `val` of the parent scope. -The workaround I chose was to mangle all variable names starting with a `___` (however special care has to be taken with recursive functions). This stops accidental namespace collisions: - -``` -function(byref __byref_ref) - set(__byref_val 1) - message("${${__byref_ref}} ${__byref_val}") -endfunction() - -``` - -So If you read some of the functions and see very strange variable names this is the explanation. - - - - -... more coming soon \ No newline at end of file diff --git a/build/after_success.cmake b/build/after_success.cmake index 5dc71b0c..980a6f2f 100644 --- a/build/after_success.cmake +++ b/build/after_success.cmake @@ -2,8 +2,7 @@ # call this script with cmake -P compile.cmake path/to/target/dir include("${CMAKE_CURRENT_LIST_DIR}/../cmakepp.cmake") - -compile_oocmake("${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/release/cmakepp.cmake") +cmakepp_compile("${CMAKE_CURRENT_SOURCE_DIR}/release/cmakepp.cmake") diff --git a/build/script.cmake b/build/script.cmake index ca3d3526..98c1304c 100644 --- a/build/script.cmake +++ b/build/script.cmake @@ -1,35 +1,8 @@ message(STATUS "running test for cmake functions") -# include oo-cmake +# include cmakepp include("${CMAKE_CURRENT_LIST_DIR}/../cmakepp.cmake") -#message("configuratiation") -#print_locals() -# glob tets -file(GLOB_RECURSE tests "${CMAKE_CURRENT_LIST_DIR}/../tests/**.cmake") -set(package_dir "${CMAKE_CURRENT_LIST_DIR}") - - -oocmake_config(temp_dir) -ans(temp_dir) -set(test_dir "${temp_dir}/test_dir") - - -#set(OOCMAKE_DEBUG_EXECUTE true) - -foreach(test ${tests}) - get_filename_component(test "${test}" REALPATH) - - file(REMOVE_RECURSE "${test_dir}") - file(MAKE_DIRECTORY "${test_dir}") - #function_import("${test}" as test_function REDEFINE) - message(STATUS "running test ${test}... ") - - cd("${test_dir}") - - - call("${test}"()) - #test_function() - message(STATUS "success!") -endforeach() \ No newline at end of file +## execute all tests in test directory +test_execute_glob("${CMAKE_CURRENT_LIST_DIR}/../tests/**.cmake" --recurse) \ No newline at end of file diff --git a/cmake/README.md b/cmake/README.md index 7931bf0e..13a51726 100644 --- a/cmake/README.md +++ b/cmake/README.md @@ -4,6 +4,7 @@ `cmakepp` has a lot of different functions. I tried to subdivide them into some meaningful sections. +* [Creating Checksums](checksum/README.md) * [Collections](collections/README.md) * [Date/Time](datetime/README.md) * [Events](events/README.md) @@ -11,6 +12,7 @@ * [Functions](function/README.md) * [Logging Functions](log/README.md) * [Maps - Structured Data in CMake](map/README.md) +* [Navigation Functions](navigation/README.md) * [Objects ](object/README.md) * [Package Management](package/README.md) * [User Data](persistence/README.md) diff --git a/cmake/ast/expr/expr_definition.cmake b/cmake/ast/expr/expr_definition.cmake index 3b099da5..e6c96621 100644 --- a/cmake/ast/expr/expr_definition.cmake +++ b/cmake/ast/expr/expr_definition.cmake @@ -8,7 +8,7 @@ map() key("name") val("tokenize") key("function") - val("token_stream_new\(/1\ /2\)") + val("token_stream_new\(/0\ /1\)") key("input") val("global") val("str") @@ -19,7 +19,7 @@ map() key("name") val("parse") key("function") - val("ast_parse\(/1\ /2\ /3\ /4\ /5\)") + val("ast_parse\(/0\ /1\ /2\ /3\ /4\)") key("input") val("tokens") val("root_definition") @@ -33,7 +33,7 @@ map() key("name") val("compile") key("function") - val("ast_eval\(/1\ /2\ /3\ /4\)") + val("ast_eval\(/0\ /1\ /2\ /3\)") key("input") val("ast") val("context") diff --git a/cmake/ast/expr_compile.cmake b/cmake/ast/expr_compile.cmake index ccdac415..b11c4d64 100644 --- a/cmake/ast/expr_compile.cmake +++ b/cmake/ast/expr_compile.cmake @@ -13,7 +13,7 @@ function(expr_compile str) language("oocmake") ans(language) if(NOT language) - oocmake_config(base_dir) + cmakepp_config(base_dir) ans(base_dir) language("${base_dir}/resources/expr.json") ans(language) diff --git a/cmake/ast/lang.cmake b/cmake/ast/lang.cmake index 74c240ec..4734f220 100644 --- a/cmake/ast/lang.cmake +++ b/cmake/ast/lang.cmake @@ -54,9 +54,16 @@ # handle function call map_tryget("${current_target}" function) ans(func) + if("${func}" MATCHES "(.*)\\(([^\\)]*)\\)$" ) + set(func "${CMAKE_MATCH_1}") + set(arg_assignments "${CMAKE_MATCH_2}") + string(REPLACE " " ";" arg_assignments "${arg_assignments}") + else() + message(FATAL_ERROR "failed to parse targets function") + endif() # curry function to specified arguments - curry2("${func}") + curry3(() => "${func}"(${arg_assignments})) ans(func) # compile argument string @@ -77,7 +84,7 @@ # call curried function - note that context is available to be modified set(func_call "${func}(${arguments_string})") - # message("lang: target '${target}' func call ${func_call}") + #message("lang: target '${target}' func call ${func_call}") set_ans("") eval("${func_call}") ans(res) diff --git a/cmake/ast/script.cmake b/cmake/ast/script.cmake index 4487c719..095eba91 100644 --- a/cmake/ast/script.cmake +++ b/cmake/ast/script.cmake @@ -8,7 +8,7 @@ function(script str) language("oocmake") ans(lang) if(NOT lang) - #oocmake_config(base_dir) + #cmakepp_config(base_dir) #ans(base_dir) #language("${base_dir}/resources/expr.json") @@ -21,7 +21,7 @@ function(script str) map_tryget("${lang}" md5) ans(language_hash) string(MD5 script_language_hash "${str}${language_hash}") - oocmake_config(temp_dir) + cmakepp_config(temp_dir) ans(temp_dir) set(obj_file "${temp_dir}/expressions/expr_${script_language_hash}.cmake") map_tryget(global expression_cache) diff --git a/cmake/cache/dir_cache.cmake b/cmake/cache/dir_cache.cmake index d18ca4b6..ec80dab4 100644 --- a/cmake/cache/dir_cache.cmake +++ b/cmake/cache/dir_cache.cmake @@ -14,7 +14,7 @@ ans(cache_key) endif() - oocmake_config(temp_dir) + cmakepp_config(temp_dir) ans(temp_dir) set(cache_dir "${temp_dir}/dir_cache/${cache_key}") diff --git a/cmake/cache/file_cache_clear_all.cmake b/cmake/cache/file_cache_clear_all.cmake index eb190398..6f149be1 100644 --- a/cmake/cache/file_cache_clear_all.cmake +++ b/cmake/cache/file_cache_clear_all.cmake @@ -1,6 +1,6 @@ function(file_cache_clear_all) - oocmake_config(temp_dir) + cmakepp_config(temp_dir) ans(temp_dir) file(REMOVE_RECURSE "${temp_dir}/file_cache") endfunction() \ No newline at end of file diff --git a/cmake/cache/file_cache_key.cmake b/cmake/cache/file_cache_key.cmake index 9e1b91f6..0d15e591 100644 --- a/cmake/cache/file_cache_key.cmake +++ b/cmake/cache/file_cache_key.cmake @@ -8,7 +8,7 @@ function(file_cache_key cache_key) endif() checksum_string("${cache_key}") ans(key) - oocmake_config(temp_dir) + cmakepp_config(cache_dir) ans(temp_dir) set(file "${temp_dir}/file_cache/_${key}.cmake") return_ref(file) diff --git a/cmake/cache/string_cache_get.cmake b/cmake/cache/string_cache_get.cmake new file mode 100644 index 00000000..62424133 --- /dev/null +++ b/cmake/cache/string_cache_get.cmake @@ -0,0 +1,9 @@ +function(string_cache_get cache_location key) + string_cache_location("${cache_location}" "${key}") + ans(location) + if(NOT EXISTS "${location}") + return() + endif() + fread("${location}/value.txt") + return_ans() +endfunction() diff --git a/cmake/cache/string_cache_hit.cmake b/cmake/cache/string_cache_hit.cmake new file mode 100644 index 00000000..2b37d4a9 --- /dev/null +++ b/cmake/cache/string_cache_hit.cmake @@ -0,0 +1,6 @@ + +function(string_cache_hit cache_location key) + string_cache_location("${cache_location}" "${key}") + ans(location) + return_truth(EXISTS "${location}") +endfunction() \ No newline at end of file diff --git a/cmake/cache/string_cache_location.cmake b/cmake/cache/string_cache_location.cmake new file mode 100644 index 00000000..fe5c811e --- /dev/null +++ b/cmake/cache/string_cache_location.cmake @@ -0,0 +1,8 @@ + +function(string_cache_location cache_location key) + cmakepp_config(cache_dir) + ans(cache_dir) + path_qualify_from("${cache_dir}" "${cache_location}/${key}") + ans(location) + return_ref(location) +endfunction() \ No newline at end of file diff --git a/cmake/cache/string_cache_return_hit.cmake b/cmake/cache/string_cache_return_hit.cmake new file mode 100644 index 00000000..7d968e87 --- /dev/null +++ b/cmake/cache/string_cache_return_hit.cmake @@ -0,0 +1,9 @@ + +macro(string_cache_return_hit cache_location key) + string_cache_hit("${cache_location}" "${key}") + ans(hit) + if( hit) + string_cache_get("${cache_location}" "${key}") + return_ans() + endif() +endmacro() diff --git a/cmake/cache/string_cache_update.cmake b/cmake/cache/string_cache_update.cmake new file mode 100644 index 00000000..67d13bf5 --- /dev/null +++ b/cmake/cache/string_cache_update.cmake @@ -0,0 +1,12 @@ + +function(string_cache_update cache_location key value) + string_cache_location("${cache_location}" "${key}") + ans(location) + if(NOT EXISTS "${location}") + fwrite("${location}/value.txt" "${value}") + return(true) + else() + fwrite("${location}/value.txt" "${value}") + return(false) + endif() +endfunction() diff --git a/cmake/checksum/README.md b/cmake/checksum/README.md new file mode 100644 index 00000000..555c4e9e --- /dev/null +++ b/cmake/checksum/README.md @@ -0,0 +1,101 @@ +# Creating Checksums + +`CMake` provides you with two hashing functions: `string()` and `file()`. These work just fine but I reformed them to work well with `cmakepp` and extended the functionality: + + + + +* [checksum_dir](#checksum_dir) +* [checksum_file](#checksum_file) +* [checksum_files](#checksum_files) +* [checksum_glob_ignore](#checksum_glob_ignore) +* [checksum_layout](#checksum_layout) +* [checksum_object](#checksum_object) +* [checksum_string](#checksum_string) + +### Function Details + +## `checksum_dir` + + `( [--algorthm = "MD5"])->` + + calculates the checksum for the specified directory + just like checksum_layout however also factors in the file's contents + + + + + +## `checksum_file` + + `( [--algorithm = "MD5"])->` + + calculates the checksum for the specified file delegates the + call to `CMake`'s file() function + + + + + +## `checksum_files` + + `( )->` + + create a checksum from specified files relative to + the checksum is influenced by the files relative paths + and the file content + + + + + +## `checksum_glob_ignore` + + `( [--algorithm = "MD5"])->` + + calculates the checksum for the specified glob ignore expressIONS + uses checksum_files internally. the checksum is unique to file content + and relative file structure + + + + + +## `checksum_layout` + + `( [--algorithm "MD5"])->` + + this method generates the checksum for the specified directory + it is done by taking every file's relative path into consideration + and generating the hash. The file's content does not influence the hash + + + + + +## `checksum_object` + + `( [--algorithm = "MD5"])->` + + this function takes any value and generates its hash + the difference to string hash is that it serializes the specified object + which lets you create the hash for the whoile object graph. + + + + + +## `checksum_string` + + `( [--algorithm = MD5])->` + ` ::= "MD5"|"SHA1"|"SHA224"|"SHA256"|"SHA384"|"SHA512"` + + this function takes any string and computes the hash value of it using the + hash algorithm specified (which defaults to MD5) + returns the checksum + + + + + + diff --git a/cmake/checksum/README.md.in b/cmake/checksum/README.md.in new file mode 100644 index 00000000..500df0a0 --- /dev/null +++ b/cmake/checksum/README.md.in @@ -0,0 +1,11 @@ +# Creating Checksums + +`CMake` provides you with two hashing functions: `string()` and `file()`. These work just fine but I reformed them to work well with `cmakepp` and extended the functionality: + +<% assign(function_files = glob("**.cmake" --relative)) %> + +<%= markdown_template_function_list(${function_files}) %> + +### Function Details + +<%= markdown_template_function_descriptions(${function_files}) %> diff --git a/cmake/checksum/checksum_dir.cmake b/cmake/checksum/checksum_dir.cmake index cdc8ac4e..7d2866a2 100644 --- a/cmake/checksum/checksum_dir.cmake +++ b/cmake/checksum/checksum_dir.cmake @@ -1,13 +1,14 @@ -# calculates and returns the checksum for the specified directory -# including file content -# uses md5 as a default, other algorithms are possible (see string or file for algorithm names) +## `( [--algorthm = "MD5"])->` +## +## calculates the checksum for the specified directory +## just like checksum_layout however also factors in the file's contents +## function(checksum_dir dir) set(args ${ARGN}) list_extract_labelled_keyvalue(args --algorithm) ans(algorithm) - path("${dir}") - ans(dir) + path_qualify(dir) file(GLOB_RECURSE files RELATIVE "${dir}" "${dir}/**") diff --git a/cmake/checksum/checksum_file.cmake b/cmake/checksum/checksum_file.cmake index 2389171a..4966ec53 100644 --- a/cmake/checksum/checksum_file.cmake +++ b/cmake/checksum/checksum_file.cmake @@ -1,10 +1,12 @@ -# calculates and returns the checksum for the specified file -# uses md5 as a default, other algorithms are possible (see string or file for algorithm names) +## `( [--algorithm = "MD5"])->` +## +## calculates the checksum for the specified file delegates the +## call to `CMake`'s file() function +## function(checksum_file file) path_qualify(file) - set(args ${ARGN}) list_extract_labelled_value(args --algorithm) ans(checksum_alg) diff --git a/cmake/checksum/checksum_files.cmake b/cmake/checksum/checksum_files.cmake index 7a2a4b75..a856ce59 100644 --- a/cmake/checksum/checksum_files.cmake +++ b/cmake/checksum/checksum_files.cmake @@ -1,29 +1,33 @@ +## `( )->` +## +## create a checksum from specified files relative to +## the checksum is influenced by the files relative paths +## and the file content +## +function(checksum_files dir) + set(args ${ARGN}) + list_extract_labelled_keyvalue(args --algorithm) + ans(algorithm) - ## paths relative to dir - function(checksum_files dir) - set(args ${ARGN}) - list_extract_labelled_keyvalue(args --algorithm) - ans(algorithm) - - list(LENGTH args len) - if(len) - list(REMOVE_DUPLICATES args) - list(SORT args) + list(LENGTH args len) + if(len) + list(REMOVE_DUPLICATES args) + list(SORT args) + endif() + + set(checksums) + foreach(file ${ARGN}) + if(EXISTS "${dir}/${file}") + checksum_file("${dir}/${file}" ${algorithm}) + ans(file_checksum) + # create checksum from file checsum and file name + checksum_string("${file_checksum}.${file}" ${algorithm}) + ans(combined_checksum) + list(APPEND checksums "${combined_checksum}") endif() - - set(checksums) - foreach(file ${ARGN}) - if(EXISTS "${dir}/${file}") - checksum_file("${dir}/${file}" ${algorithm}) - ans(file_checksum) - # create checksum from file checsum and file name - checksum_string("${file_checksum}.${file}" ${algorithm}) - ans(combined_checksum) - list(APPEND checksums "${combined_checksum}") - endif() - endforeach() + endforeach() - checksum_string("${checksums}" ${checksum_alg}) - ans(checksum_dir) - return_ref(checksum_dir) - endfunction() \ No newline at end of file + checksum_string("${checksums}" ${checksum_alg}) + ans(checksum_dir) + return_ref(checksum_dir) +endfunction() \ No newline at end of file diff --git a/cmake/checksum/checksum_glob_ignore.cmake b/cmake/checksum/checksum_glob_ignore.cmake index e04bbacc..2f992894 100644 --- a/cmake/checksum/checksum_glob_ignore.cmake +++ b/cmake/checksum/checksum_glob_ignore.cmake @@ -1,6 +1,10 @@ - - - function(checksum_glob_ignore) +## `( [--algorithm = "MD5"])->` +## +## calculates the checksum for the specified glob ignore expressIONS +## uses checksum_files internally. the checksum is unique to file content +## and relative file structure +## +function(checksum_glob_ignore) set(args ${ARGN}) list_extract_labelled_keyvalue(args --algorithm) ans(algorithm) @@ -22,4 +26,4 @@ checksum_files("${pwd}" ${normalized_files}) return_ans() - endfunction() \ No newline at end of file +endfunction() \ No newline at end of file diff --git a/cmake/checksum/checksum_layout.cmake b/cmake/checksum/checksum_layout.cmake index 2822405a..36e84fc8 100644 --- a/cmake/checksum/checksum_layout.cmake +++ b/cmake/checksum/checksum_layout.cmake @@ -1,21 +1,28 @@ -# calculates and returns the checksum for the specified directory -# without checking files themselves -# uses md5 as a default, other algorithms are possible (see string or file for algorithm names) - function(checksum_layout dir) - path("${dir}") - ans(dir) +## `( [--algorithm "MD5"])->` +## +## this method generates the checksum for the specified directory +## it is done by taking every file's relative path into consideration +## and generating the hash. The file's content does not influence the hash +## +function(checksum_layout dir) + path_qualify(dir) set(args ${ARGN}) - + list_extract_labelled_keyvalue(args --algorithm) ans(algorithm) - file(GLOB_RECURSE files RELATIVE "${dir}" "${dir}/**") + if(files) + ## todo sort. normalize paths, remove directories + string(REPLACE "//" "/" files "${files}") + list(SORT files) + endif() + checksum_string("${files}" ${algorithm}) ans(checksum_dir) return_ref(checksum_dir) - endfunction() +endfunction() diff --git a/cmake/checksum/checksum_object.cmake b/cmake/checksum/checksum_object.cmake index 864e5eb6..6ffac9a8 100644 --- a/cmake/checksum/checksum_object.cmake +++ b/cmake/checksum/checksum_object.cmake @@ -1,7 +1,11 @@ -# returns the checksum for the specified object (object graph) +## `( [--algorithm = "MD5"])->` +## +## this function takes any value and generates its hash +## the difference to string hash is that it serializes the specified object +## which lets you create the hash for the whoile object graph. +## function(checksum_object obj) - - json_serialize("${obj}") + json("${obj}") ans(json) checksum_string("${json}" ${ARGN}) return_ans() diff --git a/cmake/checksum/checksum_string.cmake b/cmake/checksum/checksum_string.cmake index 3451f124..c8259f7b 100644 --- a/cmake/checksum/checksum_string.cmake +++ b/cmake/checksum/checksum_string.cmake @@ -1,5 +1,10 @@ -# calculates and returns the checksum for the specified string -# uses md5 as a default, other algorithms are possible (see string or file for algorithm names) +## `( [--algorithm = MD5])->` +## ` ::= "MD5"|"SHA1"|"SHA224"|"SHA256"|"SHA384"|"SHA512"` +## +## this function takes any string and computes the hash value of it using the +## hash algorithm specified (which defaults to MD5) +## returns the checksum +## function(checksum_string str) set(args ${ARGN}) list_extract_labelled_value(args --algorithm) @@ -7,7 +12,6 @@ function(checksum_string str) if(NOT algorithm) set(algorithm MD5) endif() - string("${algorithm}" checksum "${str}" ) return_ref(checksum) endfunction() \ No newline at end of file diff --git a/cmake/classes/Configuration.cmake b/cmake/classes/Configuration.cmake deleted file mode 100644 index 4f9f2995..00000000 --- a/cmake/classes/Configuration.cmake +++ /dev/null @@ -1,311 +0,0 @@ -function(Configuration) - this_inherit(CommandRunner) - - proto_declarefunction(AddConfigurationFilesRecurse ) - - - - # create a field containing all configurations - map_new() - ans(configurations) - this_set(configurations ${configurations}) - - - # recursively adds configuration files to the configuration object - # starts with the given and adds all coniguration files in and its - # paretn directories up to - function(${AddConfigurationFilesRecurse} path) - # recursively add configuration files - set(current_dir "${path}") - while(true) - get_filename_component(new_current_dir "${current_dir}" PATH) - # current dir is equal to new_current_dir when current_dir is root (recursion anchor) - if("${new_current_dir}" STREQUAL "${current_dir}") - break() - endif() - set(current_dir ${new_current_dir}) - # if cutil.config exists add it to configuration - if(EXISTS "${current_dir}/cutil.config") - string_normalize("${current_dir}/cutil.config") - ans(name) - obj_member_call(${this} AddConfigurationFile "${name}" "${current_dir}/cutil.config") - endif() - endwhile() - endfunction() - - # add a named configuration file to the configuration object - # configuration files are searched in reverse order for - # configuration entries - # - proto_declarefunction(AddConfigurationFile) - function(${AddConfigurationFile} name config_file) - set(config) - message(DEBUG LEVEL 6 "Adding Configuration '${name}' @ ${config_file} ") - map_navigate_set("this.configurations.${name}.file" "${config_file}") - map_navigate_set("this.configurations.${name}.name" "${name}") - obj_member_call(${this} Load) - endfunction() - - # returns the configuration scope specified by SCOPE variable - # if no SCOPE is specified the most specialized scope is used - # ie the last one added - proto_declarefunction(GetScope) - function(${GetScope} ) - this_import(configurations) - cmake_parse_arguments("" "" "SCOPE" "" ${ARGN}) - set(config) - if(NOT _SCOPE) - map_keys(${configurations} ) - ans(keys) - list_get( keys -2) - ans(key) - map_tryget(${configurations} "${key}") - ans(config) - else() - map_tryget(${configurations} "${_SCOPE}") - ans(config) - endif() - return_ref(config) - endfunction() - - # set a configuration value in SCOPE. you can specify a navigation expression - # if SCOPE is not specified the most specialized scope is used ie the last one - # added - # - proto_declarefunction(Set) - function(${Set} navigation_expression value) - cmake_parse_arguments("" "" "SCOPE" "" ${ARGN}) - obj_member_call(${this} GetScope SCOPE "${_SCOPE}") - ans(config) - if(NOT config) - this_import(configurations) - ref_print(${configurations}) - message(FATAL_ERROR "Configuration: could not find configuration scope '${_SCOPE}'") - endif() - - map_get(${config} "file") - ans(file) - map_get(${config} "name") - ans(name) - if(EXISTS "${file}") - file(READ "${file}" json) - json_deserialize( ${json}) - ans(_config_object) - endif() - map_navigate_set("_config_object.${navigation_expression}" "${value}") - - map_navigate_set("configurations.${name}.config" ${_config_object}) - json_serialize( "${_config_object}" INDENTED) - ans(json) - file(WRITE "${file}" "${json}") - endfunction() - - # get a configuration value from SCOPE if scope is not specified - # the most specialized scope is used - proto_declarefunction(Get) - function(${Get} navigation_expression) - this_import(configurations) - cmake_parse_arguments("" "" "SCOPE" "" ${ARGN}) - obj_member_call(${this} GetScope SCOPE "${_SCOPE}") - ans(config) - if(NOT config) - message(FATAL_ERROR "Configuration: could not find configuration scope") - endif() - - map_keys(${configurations} ) - ans(keys) - list(REVERSE keys) - map_values(${configurations} "${keys}" ) - ans(configs) - if(NOT _SCOPE) - list(GET keys 0 key) - set(_SCOPE ${key}) - endif() - set(found false) - - foreach(config ${configs}) - map_navigate(config_name "config.name") - if(${found} OR ${config_name} STREQUAL "${_SCOPE}" ) - set(found true) - endif() - if(found) - map_get(${config} "file") - ans(file) - - if(NOT EXISTS "${file}") - return() - endif() - file(READ "${file}" json) - json_deserialize( ${json}) - ans(_config_object) - map_navigate(value "_config_object.${navigation_expression}") - if(value) - return_ref(value) - break() - endif() - endif() - endforeach() - return() - endfunction() - - proto_declarefunction(Save) - function(${Save}) - map_keys(${configurations} ) - ans(keys) - foreach(key ${keys}) - map_navigate(file "configurations.${key}.file") - map_navigate(cfg "configurations.${key}.config") - json_serialize( ${cfg} INDENTED) - ans(json) - file(WRITE ${file} "${json}") - endforeach() - endfunction() - - - proto_declarefunction(Load) - function(${Load}) - this_import(configurations) - map_keys(${configurations} ) - ans(keys) - set(configs) - foreach(key ${keys}) - map_navigate(file "configurations.${key}.file") - if(EXISTS "${file}") - file(READ ${file} json) - json_deserialize( "${json}") - ans(cfg) - map_navigate_set("configurations.${key}.config" ${cfg}) - list(APPEND configs ${cfg}) - endif() - endforeach() - set(config) - map_merge( ${configs}) - ans(config) - this_set(configuration ${config}) - #ref_print(${config}) - endfunction() - - - proto_declarefunction(GetCommand) - function(${GetCommand}) - cmake_parse_arguments("" "--all;--json" "--scope" "" ${ARGN}) - obj_member_call(${this} Load) - - - if(_--scope AND _--all) - set(cfg) - map_navigate(cfg "configurations.${_--scope}.config") - if(cfg) - ref_print(${cfg}) - else() - message("no configuration found") - endif() - return() - endif() - - if(_--all) - if(configuration) - ref_print(${configuration}) - else() - message("no configuration found") - endif() - return() - endif() - - if(_--scope) - map_navigate(res "configurations.${_--scope}.config.${ARGN}") - ref_print(${res}) - else() - map_navigate(res "configuration.${ARGN}") - ref_print(${res}) - endif() - - endfunction() - - - proto_declarefunction(SetCommand) - function(${SetCommand}) - cmake_parse_arguments("" "" "--scope" "" ${ARGN}) - set(scope) - if( _--scope) - set(scope SCOPE ${_--scope}) - endif() - if(NOT _UNPARSED_ARGUMENTS) - message("Configuration: not value to be set") - return() - endif() - obj_member_call(${this} Set ${_UNPARSED_ARGUMENTS} ${scope}) - endfunction() - - proto_declarefunction(PrintScopes) - function(${PrintScopes}) - map_keys(${configurations} ) - ans(keys) - message("Current Config Scopes:") - foreach(scope ${keys}) - message(" ${scope}") - endforeach() - endfunction() - - proto_declarefunction(SaveScope) - function(${SaveScope} scope) - map_tryget(${scope} "file") - ans(file) - map_tryget(${scope} config) - ans(cfg) - json_serialize( ${cfg}) - ans(res) - file(WRITE "${file}" "${res}") - endfunction() - - - this_declare_call(__call__) - function(${__call__}) - cmake_parse_arguments("" "" "--scope" "" ${ARGN}) - set(scope) - if( _--scope) - set(scope SCOPE ${_--scope}) - endif() - - obj_member_call(${this} GetScope ${scope}) - ans(__scope) - map_tryget(${__scope} config) - ans(cfg) - if(NOT cfg) - map_new() - ans(cfg) - map_set(${__scope} config ${cfg}) - endif() - - if(NOT _UNPARSED_ARGUMENTS) - - set(cfg ${configuration}) - else() - list(LENGTH _UNPARSED_ARGUMENTS len) - if(len EQUAL 1) - set(cfg ${configuration}) - endif() - endif() - - set(_UNPARSED_ARGUMENTS "cfg.${_UNPARSED_ARGUMENTS}") - - map_edit(${_UNPARSED_ARGUMENTS} --print) - obj_member_call(${this} SaveScope ${__scope}) - endfunction() - - - -# obj_bind(bound_function ${this} ${Edit}) -# obj_member_call(${this} AddCommandHandler edit ${bound_function}) - -# obj_bind(bound_function ${this} ${GetCommand}) -# obj_member_call(${this} AddCommandHandler get ${bound_function}) - -# obj_bind(bound_function ${this} ${SetCommand}) -# obj_member_call(${this} AddCommandHandler set ${bound_function}) - -# obj_bind(bound_function ${this} ${PrintScopes}) -# obj_member_call(${this} AddCommandHandler scopes ${bound_function}) - - -endfunction() \ No newline at end of file diff --git a/cmake/classes/Functor.cmake b/cmake/classes/Functor.cmake deleted file mode 100644 index cf1bdfe5..00000000 --- a/cmake/classes/Functor.cmake +++ /dev/null @@ -1,6 +0,0 @@ -function(Functor) - proto_declarefunction(call) - function(${call}) - - endfunction() -endfunction() \ No newline at end of file diff --git a/cmake/cmake/cmake_function_parse.cmake b/cmake/cmake/cmake_function_parse.cmake new file mode 100644 index 00000000..285186f7 --- /dev/null +++ b/cmake/cmake/cmake_function_parse.cmake @@ -0,0 +1,7 @@ + + function(cmake_function_parse code) + cmake_function_signature("${code}") + ans(signature) + map_set(${signature} code "${code}") + return_ref() + endfunction() diff --git a/cmake/cmake/cmake_function_rename_first.cmake b/cmake/cmake/cmake_function_rename_first.cmake new file mode 100644 index 00000000..0206f233 --- /dev/null +++ b/cmake/cmake/cmake_function_rename_first.cmake @@ -0,0 +1,12 @@ + + function(cmake_function_rename_first code new_name) + cmake_function_signature("${code}") + ans(sig) + map_tryget(${sig} signature_code) + ans(old_func) + map_tryget(${sig} name) + ans(old_name) + string(REPLACE "${old_name}" "${new_name}" new_func "${old_func}") + string(REPLACE "${old_func}" "${new_func}" code "${code}") + return_ref(code) + endfunction() diff --git a/cmake/cmake/cmake_function_signature.cmake b/cmake/cmake/cmake_function_signature.cmake new file mode 100644 index 00000000..93a03490 --- /dev/null +++ b/cmake/cmake/cmake_function_signature.cmake @@ -0,0 +1,16 @@ + + function(cmake_function_signature code) + regex_cmake() + string(REGEX MATCHALL "${regex_cmake_function_begin}" functions "${code}") + list_pop_front(functions) + ans(function) + + map_new() + ans(res) + if("${function}" MATCHES "${regex_cmake_function_signature}") + map_set(${res} name "${${regex_cmake_function_signature.name}}") + map_set(${res} args_string "${${regex_cmake_function_signature.args}}") + map_set(${res} signature_code "${function}") + endif() + return_ref(res) + endfunction() diff --git a/cmake/cmake/cmake_function_signatures.cmake b/cmake/cmake/cmake_function_signatures.cmake new file mode 100644 index 00000000..9d00b0a7 --- /dev/null +++ b/cmake/cmake/cmake_function_signatures.cmake @@ -0,0 +1,7 @@ + + function(cmake_function_signatures code) + regex_cmake() + string(REGEX MATCHALL "${regex_cmake_function_begin}" functions "${code}") + return_ref(functions) + endfunction() + diff --git a/cmake/cmake/is_cmake_function.cmake b/cmake/cmake/is_cmake_function.cmake new file mode 100644 index 00000000..f7641907 --- /dev/null +++ b/cmake/cmake/is_cmake_function.cmake @@ -0,0 +1,8 @@ + + + function(is_cmake_function code) + if("${code}" MATCHES "function.*endfunction") + return(true) + endif() + return(false) + endfunction() \ No newline at end of file diff --git a/cmake/cmakepp/cmakepp.cmake b/cmake/cmakepp/cmakepp.cmake index 00c9a90c..203867fb 100644 --- a/cmake/cmakepp/cmakepp.cmake +++ b/cmake/cmakepp/cmakepp.cmake @@ -3,7 +3,7 @@ ## ## function(cmakepp) - oocmake_config(base_dir) + cmakepp_config(base_dir) ans(base_dir) cmake("-P" "${base_dir}/cmakepp.cmake" ${ARGN}) return_ans() diff --git a/cmake/cmakepp/cmakepp_after_initialize.cmake b/cmake/cmakepp/cmakepp_after_initialize.cmake deleted file mode 100644 index 71f234ae..00000000 --- a/cmake/cmakepp/cmakepp_after_initialize.cmake +++ /dev/null @@ -1,12 +0,0 @@ - ## function which is called after cmakepp was loaded - ## this is used to create and register events or other things once - function(cmakepp_after_initialize) - ## register listener for the project_on_package_load event - ## which exports cmake files of package and calls on_load hook - event_addhandler(project_on_package_load cmakepp_project_on_package_load) - - ## register listener for the project_on_package_install event - ## which directly after a package is installed in a project - event_addhandler(project_on_package_install cmakepp_project_on_package_install) - - endfunction() diff --git a/cmake/cmakepp/cmakepp_compile.cmake b/cmake/cmakepp/cmakepp_compile.cmake index 08b02283..b438e83a 100644 --- a/cmake/cmakepp/cmakepp_compile.cmake +++ b/cmake/cmakepp/cmakepp_compile.cmake @@ -4,16 +4,15 @@ ## compiles cmakepp into a single file which is faster to include function(cmakepp_compile target_file) path_qualify(target_file) - - oocmake_config(base_dir) + cmakepp_config(base_dir) ans(base_dir) - file(STRINGS "${base_dir}/cmakepp.cmake" oocmake_file) + file(STRINGS "${base_dir}/cmakepp.cmake" cmakepp_main_file) - foreach(line ${oocmake_file}) - if("_${line}" STREQUAL "_include(\"\${oocmake_base_dir}/cmake/core/require.cmake\")") + foreach(line ${cmakepp_main_file}) + if("_${line}" STREQUAL "_include(\"\${cmakepp_base_dir}/cmake/core/require.cmake\")") - elseif("_${line}" STREQUAL "_require(\"\${oocmake_base_dir}/cmake/*.cmake\")") + elseif("_${line}" STREQUAL "_require(\"\${cmakepp_base_dir}/cmake/*.cmake\")") file(GLOB_RECURSE files "${base_dir}/cmake/**.cmake") @@ -21,6 +20,10 @@ function(cmakepp_compile target_file) file(READ "${file}" content) file(APPEND "${target_file}" "\n\n\n${content}\n\n") endforeach() + elseif("_${line}" STREQUAL "_include(\"\${cmakepp_base_dir}/cmake/task/task_enqueue\")") + file(READ "${base_dir}/cmake/task/task_enqueue" content) + file(APPEND "${target_file}" "\n\n\n${content}\n\n") + else() file(APPEND "${target_file}" "${line}\n") endif() diff --git a/cmake/cmakepp/cmakepp_compile_docs.cmake b/cmake/cmakepp/cmakepp_compile_docs.cmake index cc2f87b1..7c0358f4 100644 --- a/cmake/cmakepp/cmakepp_compile_docs.cmake +++ b/cmake/cmakepp/cmakepp_compile_docs.cmake @@ -1,7 +1,7 @@ ## ## goes through all of cmakepp's README.md.in files and generates them function(cmakepp_compile_docs) - oocmake_config(base_dir) + cmakepp_config(base_dir) ans(base_dir) file(GLOB_RECURSE template_paths "${base_dir}/**README.md.in") diff --git a/cmake/cmakepp/cmakepp_setup_environment.cmake b/cmake/cmakepp/cmakepp_setup_environment.cmake index 32d12c64..75bdb349 100644 --- a/cmake/cmakepp/cmakepp_setup_environment.cmake +++ b/cmake/cmakepp/cmakepp_setup_environment.cmake @@ -4,7 +4,7 @@ ## cmakepp - commandline interface to cmakepp ## pkg - package manager command line interface function(cmakepp_setup_environment) - oocmake_config(base_dir) + cmakepp_config(base_dir) ans(base_dir) message(STATUS "creating alias `icmake`") @@ -13,8 +13,8 @@ function(cmakepp_setup_environment) alias_create("cmakepp" "cmake -P ${base_dir}/cmakepp.cmake") message(STATUS "creating alias `pkg`") alias_create("pkg" "cmake -P ${base_dir}/cmakepp.cmake cmakepp_project_cli") - message(STATUS "setting CMAKEPP_PATH to ${base_dir} ") - shell_env_set(CMAKEPP_PATH "${base_dir}") + message(STATUS "setting CMAKEPP_PATH to ${base_dir}/cmakepp.cmake ") + shell_env_set(CMAKEPP_PATH "${base_dir}/cmakepp.cmake") endfunction() \ No newline at end of file diff --git a/cmake/collections/list_any.cmake b/cmake/collections/list_any.cmake index ee94d01f..876249fb 100644 --- a/cmake/collections/list_any.cmake +++ b/cmake/collections/list_any.cmake @@ -2,8 +2,9 @@ ## for which the predicate holds function(list_any __list_any_lst __list_any_predicate) function_import("${__list_any_predicate}" as __list_any_predicate REDEFINE) - foreach(item ${${__list_any_lst}}) - __list_any_predicate("${item}") + + foreach(__list_any_item ${${__list_any_lst}}) + __list_any_predicate("${__list_any_item}") ans(__list_any_predicate_holds) if(__list_any_predicate_holds) return(true) diff --git a/cmake/collections/list_equal.cmake b/cmake/collections/list_equal.cmake index e2a0c391..e7723411 100644 --- a/cmake/collections/list_equal.cmake +++ b/cmake/collections/list_equal.cmake @@ -62,16 +62,12 @@ function(list_equal) # depending on the comparator if(${_COMPARATOR} STREQUAL "STREQUAL") - set(lambda "(a b) eval_truth(\"\${a}\" STREQUAL \"\${b}\") \n ans(res) \n return_value(\${res})") + set(lambda "[](a b) eval_truth('{{a}}' STREQUAL '{{b}}')") elseif(${_COMPARATOR} STREQUAL "EQUAL") - set(lambda "(a b) eval_truth( \"\${a}\" EQUAL \"\${b}\") \n ans(res) \n return_value(\${res})") + set(lambda "[](a b) eval_truth('{{a}}' EQUAL '{{b}}')") else() set(lambda "${_COMPARATOR}") endif() - - # convert lambda expressin into a function string - lambda(lambda "${lambda}") - # import function string function_import("${lambda}" as __list_equal_comparator REDEFINE) @@ -82,7 +78,8 @@ function(list_equal) list(GET listA ${i} a) list(GET listB ${i} b) #message("comparing ${a} ${b}") - __list_equal_comparator(res ${a} ${b}) + __list_equal_comparator(${a} ${b}) + ans(res) if(NOT res) return(false) endif() diff --git a/cmake/collections/list_remove_at.cmake b/cmake/collections/list_remove_at.cmake index cd575aed..a92e8c32 100644 --- a/cmake/collections/list_remove_at.cmake +++ b/cmake/collections/list_remove_at.cmake @@ -10,8 +10,6 @@ function(list_remove_at __list_remove_at_lst) ans(res) list(APPEND args ${res}) endforeach() - #list_select("${__list_remove_at_lst}" "(idx)->list_normalize_index($__list_remove_at_lst $idx)") - #ans(args) diff --git a/cmake/collections/list_reverse.cmake b/cmake/collections/list_reverse.cmake new file mode 100644 index 00000000..2b3e7741 --- /dev/null +++ b/cmake/collections/list_reverse.cmake @@ -0,0 +1,8 @@ +## `()->` +## +## reverses the specified lists elements +macro(list_reverse __list_reverse_lst) + if(${__list_reverse_lst}) + list(REVERSE ${__list_reverse_lst}) + endif() +endmacro() \ No newline at end of file diff --git a/cmake/config/config_setup.cmake b/cmake/config/config_setup.cmake index 725355c0..a5da6a75 100644 --- a/cmake/config/config_setup.cmake +++ b/cmake/config/config_setup.cmake @@ -8,5 +8,6 @@ function(config_setup name definition) map_tryget(${config} unused) ans(args) map_set(global unused_command_line_args ${args}) - curry(config_function("${config}" "${definition}" /1) as "${name}") + #curry(config_function("${config}" "${definition}" /1) as "${name}") + curry3("${name}"(a) => config_function("${config}" "${definition}" /a)) endfunction() \ No newline at end of file diff --git a/cmake/core/cmake/cmake.cmake b/cmake/core/cmake/cmake.cmake index 7b246459..18013df0 100644 --- a/cmake/core/cmake/cmake.cmake +++ b/cmake/core/cmake/cmake.cmake @@ -1,11 +1,5 @@ # convenience function for accessing cmake -# use cd() to navigate to working directory -# usage is same as cmake command line client -# syntax differs: cmake arg1 arg2 ... -> cmake(arg1 arg2 ...) -# add a --result flag to get a object containing return code -# input args etc. -# else only console output is returned function(cmake) wrap_executable(cmake "${CMAKE_COMMAND}") cmake(${ARGN}) diff --git a/cmake/core/compile.cmake b/cmake/core/compile.cmake deleted file mode 100644 index 70c3fc9a..00000000 --- a/cmake/core/compile.cmake +++ /dev/null @@ -1,58 +0,0 @@ - -function(compile files include_dirs) - - function(_compile files include_dirs already_included compiled_result) - - foreach(file ${files}) - # message("compiling ${file}") - file_find("${file}" "${include_dirs}" ".cmake") - ans(full_path) - - if(NOT full_path) - message(FATAL_ERROR "failed to find '${file}'") - endif() - # message("found at ${full_path}") - map_tryget(${already_included} "${full_path}") - ans(was_compiled) - - if(NOT was_compiled) - map_set(${already_included} "${full_path}" true) - # message("first encounter of ${full_path}") - file(READ "${full_path}" content ) - # message("content ${content}") - string(REGEX MATCHALL "[ \t]*require\\(([^\\)]+)\\)[\r\t ]*\n" matches "${content}") - string(REGEX REPLACE "[ \t]*require\\(([^\\)]+)\\)[\r\t ]*\n" "\\n" content "${content}") - - set(required_files) - foreach(match ${matches}) - string(STRIP "${match}" match) - string(REGEX REPLACE "require\\(([^\\)]+)\\)" "\\1" match "${match}") - list(APPEND required_files "${match}") - endforeach() - # message("required_files ${required_files}") - - # message_indent_push() - _compile("${required_files}" "${include_dirs};${current_dir}" "${already_included}" "${compiled_result}") - # message_indent_pop() - ans(compiled) - # messagE("appending result: ${content}") - ref_append_string(${compiled_result} " -${compiled} -## ${full_path} -${content}") - - - endif() - endforeach() - - endfunction() - map_new() - ans(already_included) - ref_new() - ans(compiled_result) - _compile("${files}" "${include_dirs}" "${already_included}" "${compiled_result}") - ref_get(${compiled_result} ) - ans(res) - # message("yay ${res}") - return_ref(res) -endfunction() \ No newline at end of file diff --git a/cmake/core/compile_oocmake.cmake b/cmake/core/compile_oocmake.cmake deleted file mode 100644 index 8fcc9655..00000000 --- a/cmake/core/compile_oocmake.cmake +++ /dev/null @@ -1,31 +0,0 @@ -## compile_oocmake() -## -## compiles cmakepp into a single file which is faster to include -function(compile_oocmake source_dir target) - set(base_dir ${source_dir}) - -# file(READ "${base_dir}/resources/expr.json" data) -# get_filename_component(res "${target}" "PATH") - - # file(WRITE "${res}/resources/expr.json" "${data}") - - - file(STRINGS "${base_dir}/cmakepp.cmake" oocmake_file) - - foreach(line ${oocmake_file}) - if("_${line}" STREQUAL "_include(\"\${oocmake_base_dir}/cmake/core/require.cmake\")") - - elseif("_${line}" STREQUAL "_require(\"\${oocmake_base_dir}/cmake/*.cmake\")") - - file(GLOB_RECURSE files "${base_dir}/cmake/**.cmake") - - foreach(file ${files} ) - file(READ "${file}" content) - file(APPEND "${target}" "\n\n\n${content}\n\n") - endforeach() - else() - file(APPEND "${target}" "${line}\n") - endif() - endforeach() -endfunction() - diff --git a/cmake/core/eval.cmake b/cmake/core/eval.cmake index 77252519..84b02bf8 100644 --- a/cmake/core/eval.cmake +++ b/cmake/core/eval.cmake @@ -12,7 +12,7 @@ function(eval code) # retrieve current ans value ans(__eval_current_ans) - oocmake_config(temp_dir) + cmakepp_config(temp_dir) ans(__eval_temp_dir) file_random( "${__eval_temp_dir}/eval_{{id}}.cmake") @@ -27,7 +27,7 @@ function(eval code) include(${__eval_file_name}) ans(res) - oocmake_config(keep_temp) + cmakepp_config(keep_temp) ans(keep_temp) if(NOT keep_temp) file(REMOVE ${__eval_file_name}) diff --git a/cmake/core/eval_ref.cmake b/cmake/core/eval_ref.cmake index c8d235cf..2fe140a6 100644 --- a/cmake/core/eval_ref.cmake +++ b/cmake/core/eval_ref.cmake @@ -2,7 +2,7 @@ # scope of invokation macro(eval_ref __eval_ref_theref) ans(__eval_ref_current_ans) - oocmake_config(temp_dir) + cmakepp_config(temp_dir) ans(__eval_ref_dir) file_random( "${__eval_ref_dir}/eval_{{id}}.cmake") @@ -14,7 +14,7 @@ macro(eval_ref __eval_ref_theref) ans(__eval_ref_res) - oocmake_config(keep_temp) + cmakepp_config(keep_temp) ans(__eval_ref_keep_temp) if(NOT __eval_ref_keep_temp) file(REMOVE ${__eval_ref_filename}) diff --git a/cmake/core/execute.cmake b/cmake/core/execute.cmake deleted file mode 100644 index a081c3a5..00000000 --- a/cmake/core/execute.cmake +++ /dev/null @@ -1,73 +0,0 @@ - - # input: - # { - # >, // path to executable or executable name -> shoudl be renamed to command - # >, // command line arguments to executable, use string_semicolon_encode() on an argument if you want to pass an argument with semicolons - # ], // timout - # >, // current working dir (default is whatever pwd returns) - # - # } - # returns: - # { - # path: ..., - # args: ..., - # > ..., - # > ..., - # output: , // all output of the process (stderr, and stdout) - # result: // return code of the process (normally 0 indicates success) - # } - # - # - function(execute) - process_start_info(${ARGN}) - ans(processStart) - - if(NOT processStart) - return() - endif() - - #obj("${processStart}") - - map_clone_deep(${processStart}) - ans(processResult) - - scope_import_map(${processStart}) - - set(timeout TIMEOUT ${timeout}) - set(cwd WORKING_DIRECTORY "${cwd}") - - - command_line_args_combine(${args}) - ans(arg_string) - - ## todo - test this - string(REPLACE \\ \\\\ arg_string "${arg_string}") - - set(execute_process_command " - execute_process( - COMMAND \"\${command}\" ${arg_string} - \${timeout} - \${cwd} - RESULT_VARIABLE result - OUTPUT_VARIABLE output - ERROR_VARIABLE output - ) - - map_set(\${processResult} output \"\${output}\") - map_set(\${processResult} stdout \"\${output}\") - map_set(\${processResult} result \"\${result}\") - map_set(\${processResult} error \"\${result}\") - map_set(\${processResult} return_code \"\${result}\") - ") - - - - eval("${execute_process_command}") - - - if(OOCMAKE_DEBUG_EXECUTE) - json_print(${processResult}) - endif() - - return(${processResult}) - endfunction() \ No newline at end of file diff --git a/cmake/core/identifier.cmake b/cmake/core/identifier.cmake new file mode 100644 index 00000000..cc9c392b --- /dev/null +++ b/cmake/core/identifier.cmake @@ -0,0 +1,20 @@ + +## +## +## returns an identifier of the form `__{ARGN}_{unique}` +## the idetnfier will not be a defined function +## nor a defined variable, nor a existing global +## property. it is unique to the execution of cmake +## and can be used as a function name +function(identifier) + string_codes() + while(true) + make_guid() + ans(guid) + set(identifier "__${ARGN}_${guid}") + if(NOT COMMAND "${identifier}" AND NOT "${identifier}") + return_ref(identifier) + endif() + endwhile() + message(FATAL_ERROR "code never reached") +endfunction() \ No newline at end of file diff --git a/cmake/core/include_glob.cmake b/cmake/core/include_glob.cmake index c4f5b3e6..bf16cd1b 100644 --- a/cmake/core/include_glob.cmake +++ b/cmake/core/include_glob.cmake @@ -1,8 +1,8 @@ -#includes all files identified by globbing expressions -# see file_glob on globbing expressions +## includes all files identified by globbing expressions +## see `glob` on globbing expressions function(include_glob) set(args ${ARGN}) - file_glob(${args}) + glob(${args}) ans(files) foreach(file ${files}) include_once("${file}") diff --git a/cmake/core/make_guid.cmake b/cmake/core/make_guid.cmake index 2facae2d..1a4f46b9 100644 --- a/cmake/core/make_guid.cmake +++ b/cmake/core/make_guid.cmake @@ -1,6 +1,6 @@ #creates a unique id -function(make_guid out_id) +function(make_guid) string(RANDOM LENGTH 10 id) - set(${out_id} ${id} PARENT_SCOPE) + set(__ans ${id} PARENT_SCOPE) endfunction() diff --git a/cmake/core/oocmake.cmake b/cmake/core/oocmake.cmake deleted file mode 100644 index 2fee92f4..00000000 --- a/cmake/core/oocmake.cmake +++ /dev/null @@ -1,9 +0,0 @@ -# returns a config value -function(oocmake key) - - if(${ARGN}) - set_property(GLOBAL PROPERTY "oocmake.${key}" "${ARGN}") - endif() - get_property(res GLOBAL PROPERTY "oocmake.${key}") - set("${key}" "${res}" PARENT_SCOPE) -endfunction() \ No newline at end of file diff --git a/cmake/core/require.cmake b/cmake/core/require.cmake index d1b7510a..ae4611fa 100644 --- a/cmake/core/require.cmake +++ b/cmake/core/require.cmake @@ -10,52 +10,3 @@ function(require file) endforeach() endfunction() - -#require(require_map) -#require(map_get) -#require(map_tryget) -#require(map_set) -#require(ans) -#require(return_ref) -#require(return) -#require(file_find) -#require(stack_peek) -#require(stack_push) -#require(stack_pop) -#function(require file) -# message("require!!") -# require_map() -# ans(require_map) -# map_get(${require_map} include_dirs) -# ans(stack) - # message("stack is ${stack}") -# stack_peek(${stack}) -# ans(include_dirs) -# #message("include dirs are ${include_dirs}") -# -# get_filename_component(extension "${file}" EXT) -# if(NOT extension) -# set(file "${file}.cmake") -# endif() -# -# file_find("${file}" "${include_dirs}" "") -# ans(result_file) -# -# if(NOT result_file) -# message(FATAL_ERROR "could not find file '${file}'") -# endif() -# -# map_tryget(${require_map} ${result_file}) -# ans(was_included) -# if(was_included) -# return() -# endif() -# -# get_filename_component(directory "${result_file}" PATH) -# stack_push(${stack} ${include_dirs} ${directory}) -# include(${result_file}) -# stack_pop(${stack}) -# map_set(${require_map} "${result_file}" true) -# -# return_ref(result_file) -#endfunction() diff --git a/cmake/core/return/return.cmake b/cmake/core/return/return.cmake index 58d6e256..52841f2e 100644 --- a/cmake/core/return/return.cmake +++ b/cmake/core/return/return.cmake @@ -1,3 +1,7 @@ +## +## +## when not to use: if your data degrades when evaluated by a macro +## for example escapes are resolved macro(return) set(__ans "${ARGN}" PARENT_SCOPE) _return() diff --git a/cmake/core/topsort.cmake b/cmake/core/topsort.cmake index 79557170..f8827ec3 100644 --- a/cmake/core/topsort.cmake +++ b/cmake/core/topsort.cmake @@ -5,10 +5,14 @@ # this function will return nothing if there was a cycle or if no input was given # else it will return the topological order of the graph function(topsort get_hash expand) + + function_import("${get_hash}" as __topsort_get_hash REDEFINE) + function_import("${expand}" as __topsort_expand REDEFINE) + # visitor function function(topsort_visit result visited node) # get hash for current node - call("${get_hash}" ("${node}")) + __topsort_get_hash("${node}") ans(hash) map_tryget("${visited}" "${hash}") @@ -20,7 +24,7 @@ function(topsort get_hash expand) endif() if(NOT mark) map_set("${visited}" "${hash}" temp) - call("${expand}" ("${node}")) + __topsort_expand("${node}") ans(successors) # visit successors foreach(successor ${successors}) @@ -50,7 +54,7 @@ function(topsort get_hash expand) # select unmarked node and visit foreach(node ${ARGN}) # get hash for node - call("${get_hash}" ("${node}")) + __topsort_get_hash("${node}") ans(hash) # get marking diff --git a/cmake/datetime/datetime.cmake b/cmake/datetime/datetime.cmake index 7e9df494..db2684a2 100644 --- a/cmake/datetime/datetime.cmake +++ b/cmake/datetime/datetime.cmake @@ -52,7 +52,7 @@ function(datetime) return("${dt}") else() - message(WARNING "oocmake's datetime is not implemented for your system") + message(WARNING "cmakepp's datetime is not implemented for your system") set(yyyy) set(MM) set(dd) diff --git a/cmake/debugging/print_vars.cmake b/cmake/debugging/print_vars.cmake index 0c3e7422..0d532402 100644 --- a/cmake/debugging/print_vars.cmake +++ b/cmake/debugging/print_vars.cmake @@ -6,11 +6,19 @@ ## output: ## varA: '1' varB: 'abc' function(print_vars) + set(args "${ARGN}") + list_extract_flag(args --plain) + ans(plain) set(__str) - foreach(arg ${ARGN}) + foreach(arg ${args}) assign(____cur = ${arg}) - json("${____cur}") - ans(____cur) + if(NOT plain) + json("${____cur}") + ans(____cur) + else() + set(____cur "'${____cur}'") + endif() + string_shorten("${____cur}" "300") ans(____cur) set(__str "${__str} ${arg}: ${____cur}") diff --git a/cmake/events/README.md b/cmake/events/README.md index 32f217c7..4297f588 100644 --- a/cmake/events/README.md +++ b/cmake/events/README.md @@ -37,3 +37,183 @@ assert(EQUALS ${result} answer1 "i am an argument") * `event_removehandler( )` removes the specified event handler from the handler list (it is no longer invoked when event is emitted) * `event_emit( [arg ...]) -> any[]` invokes the event identified by `` calls every handler passing along the argument list. every eventhandler's return value is concatenated and returned. It is possible to register event handlers during call of the event itself the emit routine continues as long as their are uncalled registered event handlers but does not call them twice. * ... (functions for dynamic events, access to all available events) + + + + +### Function List + + +* [event](#event) +* [events](#events) +* [events_track](#events_track) +* [event_addhandler](#event_addhandler) +* [event_cancel](#event_cancel) +* [event_clear](#event_clear) +* [event_emit](#event_emit) +* [event_get](#event_get) +* [event_handler](#event_handler) +* [event_handlers](#event_handlers) +* [event_handler_call](#event_handler_call) +* [event_new](#event_new) +* [event_removehandler](#event_removehandler) +* [is_event](#is_event) + + +### Function Descriptions + +## `event` + + `():` + + tries to get the `` identified by `` + if it does not exist a new `` is created by [`event_new(...)`](#event_new) + + + + +## `events` + + `()-> ` + + returns the global events map it contains all registered events. + + + + +## `events_track` + + `()->` + + sets up a function which listens only to the specified events + + + + + +## `event_addhandler` + + `event_addhandler(<~event> <~callable>)->` + + adds an event handler to the specified event. returns an `` + which can be used to remove the handler from the event. + + + + + +## `event_cancel` + + `()->` + + only usable inside event handlers. cancels the current event and returns + after this handler. + + + + +## `event_clear` + + `(<~event>)->` + + removes all handlers from the specified event + + + + +## `event_emit` + + `(<~event> >)->` + + emits the specified event. goes throug all event handlers registered to + this event and + if event handlers are added during an event they will be called as well + + if a event calls event_cancel() + all further event handlers are disregarded + + returns the accumulated result of the single event handlers + + + + +## `event_get` + + `(<~event>)->` + + returns the `` identified by `` + if the event does not exist `` is returned. + + + + +## `event_handler` + + `(<~callable>)->` + + creates an from the specified callable + and returns it. a `event_handler` is also a callable + + + + +## `event_handlers` + + `()->` + + returns all handlers registered for the event + + + + +## `event_handler_call` + + `( )->` + + calls the specified event handler for the specified event. + + + + +## `event_new` + + `()->` + + creates an registers a new event which is identified by + `` if the id is not specified a unique id is generated + and used. + + returns a new object: + { + event_id: + handlers: + ... (psibbly cancellable, aggregations) + } + also defines a global function called `` which can be used to emit the event + + + + + +## `event_removehandler` + + `()->` + + removes the specified handler from the event idenfied by event_id + returns true if the handler was removed + + + + +## `is_event` + + `()->` + + returns true if the specified value is an event + an event is a ref which is callable and has an event_id + + + + + + diff --git a/cmake/events/README.md.in b/cmake/events/README.md.in index 32f217c7..18040fb1 100644 --- a/cmake/events/README.md.in +++ b/cmake/events/README.md.in @@ -37,3 +37,17 @@ assert(EQUALS ${result} answer1 "i am an argument") * `event_removehandler( )` removes the specified event handler from the handler list (it is no longer invoked when event is emitted) * `event_emit( [arg ...]) -> any[]` invokes the event identified by `` calls every handler passing along the argument list. every eventhandler's return value is concatenated and returned. It is possible to register event handlers during call of the event itself the emit routine continues as long as their are uncalled registered event handlers but does not call them twice. * ... (functions for dynamic events, access to all available events) + + + +<% + assign(function_files = glob("**.cmake" --relative)) +%> +### Function List + +<%= markdown_template_function_list(${function_files}) %> + + +### Function Descriptions + +<%= markdown_template_function_descriptions(${function_files}) %> diff --git a/cmake/events/event.cmake b/cmake/events/event.cmake index b301d775..546cd21f 100644 --- a/cmake/events/event.cmake +++ b/cmake/events/event.cmake @@ -1,11 +1,18 @@ -# returns an exisitng event or a new event -function(event name) - event_get("${name}") - ans(event) +## `():` +## +## tries to get the `` identified by `` +## if it does not exist a new `` is created by @markdown_see_function("event_new(...)") +function(event ) + set(event_id ${ARGN}) + set(event) + if(event_id) + event_get("${event_id}") + ans(event) + endif() + if(NOT event) - event_new("${name}") + event_new(${event_id}) ans(event) endif() - return_ref(event) -endfunction() \ No newline at end of file +endfunction() diff --git a/cmake/events/event_addhandler.cmake b/cmake/events/event_addhandler.cmake index 307b5c3c..fa0fb4aa 100644 --- a/cmake/events/event_addhandler.cmake +++ b/cmake/events/event_addhandler.cmake @@ -1,17 +1,18 @@ -## event_addhandler() +## `event_addhandler(<~event> <~callable>)->` ## -## adds an event handler to the event specified by name +## adds an event handler to the specified event. returns an `` +## which can be used to remove the handler from the event. ## -function(event_addhandler name handler) - event("${name}") +function(event_addhandler event handler) + event("${event}") ans(event) - ## todo - maybe escape handler string semicolon? - ## todo import handler function and create a mapping from handler->fuction + event_handler("${handler}") + ans(handler) + ## then only append function map_append_unique("${event}" handlers "${handler}") - return() + return(${handler}) endfunction() - diff --git a/cmake/events/event_cancel.cmake b/cmake/events/event_cancel.cmake index eb0de602..89152522 100644 --- a/cmake/events/event_cancel.cmake +++ b/cmake/events/event_cancel.cmake @@ -1,7 +1,7 @@ -## event_cancel()-> +## `()->` ## -## only for inside event handlers -## causes event_emit not to continue with event +## only usable inside event handlers. cancels the current event and returns +## after this handler. function(event_cancel) ref_set(${__current_event_cancel} true) return() diff --git a/cmake/events/event_clear.cmake b/cmake/events/event_clear.cmake index 0ec6b069..13a6b4ea 100644 --- a/cmake/events/event_clear.cmake +++ b/cmake/events/event_clear.cmake @@ -1,13 +1,15 @@ +## `(<~event>)->` +## ## removes all handlers from the specified event -function(event_clear event_name) - event_get("${event_name}") +function(event_clear event) + event_get("${event}") ans(event) - event_handlers("${event_name}") + event_handlers("${event}") ans(handlers) foreach(handler ${handlers}) - event_removehandler("${event_name}" "${handler}") + event_removehandler("${event}" "${handler}") endforeach() return() diff --git a/cmake/events/event_emit.cmake b/cmake/events/event_emit.cmake index f28a0539..2aaff32f 100644 --- a/cmake/events/event_emit.cmake +++ b/cmake/events/event_emit.cmake @@ -1,53 +1,62 @@ -## event_emit +## `(<~event> >)->` ## -## emits the specified event -## calls all registered event handlers for event '' +## emits the specified event. goes throug all event handlers registered to +## this event and ## if event handlers are added during an event they will be called as well +## ## if a event calls event_cancel() ## all further event handlers are disregarded -function(event_emit event_name) - event_get("${event_name}") - ans(event) - set(result) +## +## returns the accumulated result of the single event handlers +function(event_emit event) + is_event("${event}") + ans(is_event) + + if(NOT is_event) + event_get("${event}") + ans(event) + endif() - if(event) - set(previous_handlers) - # loop aslong as new event handlers are appearing - # - ref_new() - ans(__current_event_cancel) - ref_set(${__current_event_cancel} false) - while(true) - ## - map_tryget(${event} handlers) - ans(handlers) - list(REMOVE_ITEM handlers ${previous_handlers} "") - list(APPEND previous_handlers ${handlers}) - - list_length(handlers) - ans(length) - if(NOT "${length}" GREATER 0) - break() - endif() - foreach(handler ${handlers}) - rcall(success = "${handler}"(${ARGN})) - list(APPEND result "${success}") - ## check if cancel is requested - ref_get(${__current_event_cancel}) - ans(break) - if(break) - return_ref(result) - endif() - endforeach() - endwhile() + if(NOT event) + return() endif() - if(NOT "${event_name}" STREQUAL "on_event") - event_emit(on_event "${event_name}" "${ARGN}") - return_ans() - endif() + + set(result) + + set(previous_handlers) + # loop aslong as new event handlers are appearing + # + ref_new() + ans(__current_event_cancel) + ref_set(${__current_event_cancel} false) + while(true) + ## + map_tryget(${event} handlers) + ans(handlers) + list_remove(handlers ${previous_handlers} "") + list(APPEND previous_handlers ${handlers}) + + list_length(handlers) + ans(length) + if(NOT "${length}" GREATER 0) + break() + endif() + + foreach(handler ${handlers}) + + event_handler_call(${event} ${handler} ${ARGN}) + ans(success) + list(APPEND result "${success}") + ## check if cancel is requested + ref_get(${__current_event_cancel}) + ans(break) + if(break) + return_ref(result) + endif() + endforeach() + endwhile() return_ref(result) endfunction() - diff --git a/cmake/events/event_get.cmake b/cmake/events/event_get.cmake index 3bab9560..4c84656e 100644 --- a/cmake/events/event_get.cmake +++ b/cmake/events/event_get.cmake @@ -1,7 +1,18 @@ -# returns the event identified by name -function(event_get name) +## `(<~event>)->` +## +## returns the `` identified by `` +## if the event does not exist `` is returned. +function(event_get event) events() ans(events) - map_tryget(${events} "${name}") + + is_event("${event}") + ans(is_event) + + if(is_event) + return_ref(event) + endif() + + map_tryget(${events} "${event}") return_ans() endfunction() diff --git a/cmake/events/event_handler.cmake b/cmake/events/event_handler.cmake new file mode 100644 index 00000000..5282d323 --- /dev/null +++ b/cmake/events/event_handler.cmake @@ -0,0 +1,9 @@ +## `(<~callable>)->` +## +## creates an from the specified callable +## and returns it. a `event_handler` is also a callable +function(event_handler callable) + callable("${callable}") + ans(event_handler) + return_ref(event_handler) +endfunction() diff --git a/cmake/events/event_handler_call.cmake b/cmake/events/event_handler_call.cmake new file mode 100644 index 00000000..48d20285 --- /dev/null +++ b/cmake/events/event_handler_call.cmake @@ -0,0 +1,8 @@ +## `( )->` +## +## calls the specified event handler for the specified event. +function(event_handler_call event event_handler) + callable_call("${event_handler}" ${ARGN}) + ans(res) + return_ref(res) +endfunction() diff --git a/cmake/events/event_handlers.cmake b/cmake/events/event_handlers.cmake index 6f8185d7..42bcd9ca 100644 --- a/cmake/events/event_handlers.cmake +++ b/cmake/events/event_handlers.cmake @@ -1,8 +1,8 @@ -## event_handlers +## `()->` ## -## returns the handlers registered for event -function(event_handlers event_name) - event_get("${event_name}") +## returns all handlers registered for the event +function(event_handlers event) + event_get("${event}") ans(event) if(NOT event) diff --git a/cmake/events/event_new.cmake b/cmake/events/event_new.cmake index 2d0c248e..415f7f5f 100644 --- a/cmake/events/event_new.cmake +++ b/cmake/events/event_new.cmake @@ -1,13 +1,61 @@ -# creates an registers a new event -function(event_new name) - events() - ans(events) +## `()->` +## +## creates an registers a new event which is identified by +## `` if the id is not specified a unique id is generated +## and used. +## +## returns a new object: +## { +## event_id: +## handlers: +## ... (psibbly cancellable, aggregations) +## } +## also defines a global function called `` which can be used to emit the event +## +function(event_new) + set(event_id ${ARGN}) + if(NOT event_id) + identifier(event) + ans(event_id) + endif() + + if(COMMAND ${event_id}) + message(FATAL_ERROR "specified event already exists") + endif() - map_new() + ## curry the event emit function and create a callable from the event + curry3(${event_id}() => event_emit("${event_id}" /*)) ans(event) - map_set(${event} name "${name}") - map_set(${events} "${name}" ${event}) - + callable("${event}") + ans(event) + + + curry3(() => event_addhandler("${event_id}" /*)) + ans(add_handler) + + curry3(() => event_removehandler("${event_id}" /*)) + ans(remove_handler) + + curry3(() => event_clear("${event_id}" /*)) + ans(clear) + + + + ## set event's properties + map_set(${event} event_id "${event_id}") + map_set(${event} handlers) + map_set(${event} add ${add_handler}) + map_set(${event} remove ${remove_handler}) + map_set(${event} clear ${clear}) + + ## register event globally + events() + ans(events) + map_set(${events} "${event_id}" ${event}) + return(${event}) -endfunction() \ No newline at end of file +endfunction() + + + diff --git a/cmake/events/event_removehandler.cmake b/cmake/events/event_removehandler.cmake index 3ee606f7..4daa7e35 100644 --- a/cmake/events/event_removehandler.cmake +++ b/cmake/events/event_removehandler.cmake @@ -1,14 +1,25 @@ -# removes the specified handler from the event idenfied by name -function(event_removehandler name handler) - event_get("${name}") +## `()->` +## +## removes the specified handler from the event idenfied by event_id +## returns true if the handler was removed +function(event_removehandler event handler) + + event("${event}") ans(event) + if(NOT event) return(false) endif() + + event_handler("${handler}") + ans(handler) + + map_remove_item("${event}" handlers "${handler}") ans(success) return_truth("${success}") -endfunction() \ No newline at end of file +endfunction() + diff --git a/cmake/events/events.cmake b/cmake/events/events.cmake index 1b5c9689..2dbe6c01 100644 --- a/cmake/events/events.cmake +++ b/cmake/events/events.cmake @@ -1,4 +1,6 @@ -# returns the global events map +## `()-> ` +## +## returns the global events map it contains all registered events. function(events) function(events) diff --git a/cmake/events/events_track.cmake b/cmake/events/events_track.cmake new file mode 100644 index 00000000..4ad2eef6 --- /dev/null +++ b/cmake/events/events_track.cmake @@ -0,0 +1,32 @@ +## `()->` +## +## sets up a function which listens only to the specified events +## +function(events_track) + function_new() + ans(function_name) + + map_new() + ans(map) + + eval(" + function(${function_name}) + map_new() + ans(event_args) + map_tryget(\${event} event_id) + ans(event_id) + map_set(\${event_args} id \${event_id}) + map_set(\${event_args} args \${ARGN}) + map_set(\${event_args} event \${event}) + map_append(${map} \${event_id} \${event_args}) + map_append(${map} event_ids \${event_id}) + return(\${event_args}) + endfunction() + ") + + foreach(event ${ARGN}) + event_addhandler(${event} ${function_name}) + endforeach() + + return(${map}) +endfunction() \ No newline at end of file diff --git a/cmake/events/is_event.cmake b/cmake/events/is_event.cmake new file mode 100644 index 00000000..1550c7f7 --- /dev/null +++ b/cmake/events/is_event.cmake @@ -0,0 +1,25 @@ +## `()->` +## +## returns true if the specified value is an event +## an event is a ref which is callable and has an event_id +## +function(is_event event) + ref_isvalid("${event}") + ans(is_ref) + if(NOT is_ref) + return() + endif() + is_callable("${event}") + ans(is_callable) + if(NOT is_callable) + return(false) + endif() + + map_has(${event} event_id) + ans(has_event_id) + if(NOT has_event_id) + return(false) + endif() + + return(true) +endfunction() \ No newline at end of file diff --git a/cmake/expr/expr.cmake b/cmake/expr/expr.cmake deleted file mode 100644 index 20831f18..00000000 --- a/cmake/expr/expr.cmake +++ /dev/null @@ -1,105 +0,0 @@ - # evaluates a oo-cmake expression - function(expr ) - return_reset() - - set(expressions ${ARGN}) - - list(LENGTH expressions len) - if(${len} EQUAL 0) - return() - endif() - - - # multiple expressions - if(${len} GREATER 1) - set(result) - foreach(part ${expressions}) - expr("${part}") - ans(res) - list(APPEND result "${res}") - endforeach() - return_ref(result) - endif() - - set(expr "${expressions}") - - # string_nested_split("${expr}" { }) - #ans(parts) - - # empty expression - #list(LENGTH parts len) - #if(${len} EQUAL 0) - # return() - #endif() - - - # single expreession - # message("single expr: ${expr}") - string(STRIP "${expr}" expr) - - #exression is string - expr_string_isvalid("${expr}") - ans(is_string) - if(is_string) - #message("isstring") - expr_string_parse("${expr}") - return_ans() - endif() - - expr_integer_isvalid("${expr}") - ans(is_integer) - if(is_integer) - return_ref(expr) - endif() - - string_char_at( 0 "${expr}" ) - ans(first_char) - if("${first_char}" STREQUAL "*") - string_slice("${expr}" 1 -1) - ans(ref) - ref_get(${ref}) - ans(val) - return_ans() - endif() - - - - expr_navigate_isvalid("${expr}") - ans(is_navigation) - if(is_navigation) - #message("is navigation ${expr}") - expr_navigate("${expr}") - return_ans() - endif() - - expr_indexer_isvalid("${expr}") - ans(is_indexer) - if(is_indexer) - # message("is_indexer ${expr}") - expr_indexer("${expr}") - return_ans() - endif() - - expr_call_isvalid("${expr}") - ans(iscall) - if(iscall) - expr_call("${expr}") - return_ans() - endif() - - expr_function_isvalid("${expr}") - ans(isfunction) - if(isfunction) - expr_function("${expr}") - return_ans() - endif() - - if(DEFINED "${expr}") - return_ref("${expr}") - endif() - - - return() - - - endfunction() \ No newline at end of file diff --git a/cmake/expr/expr_assign_lvalue.cmake b/cmake/expr/expr_assign_lvalue.cmake deleted file mode 100644 index 5d71851d..00000000 --- a/cmake/expr/expr_assign_lvalue.cmake +++ /dev/null @@ -1,146 +0,0 @@ -#‡† - function(expr_assign_lvalue lvalue rvalue scope) - message("assigning ${lvalue} = ${rvalue}") - - set(regex_identifier "[a-zA-Z0-9-_]+") - - string(REPLACE ";" "†" lvalue "${lvalue}") - - - if(NOT "${first_char}" STREQUAL "[") - set(lvalue ".${lvalue}") - endif() - - - string_nested_split("${lvalue}" "[" "]") - ans(splits) - message("splits ${splits}") - set(lvalue) - - - foreach(split ${splits}) - if("${split}" MATCHES "^\\[.+\\]$") - string_slice("${split}" 1 -2) - ans(inner_split) - expr("${inner_split}") - ans(split) - set(split "[${split}]") - endif() - list(APPEND lvalue "${split}") - endforeach() - string(REPLACE ";" "" lvalue "${lvalue}") - string(REPLACE "†" ";" lvalue "${lvalue}") - string(REPLACE "." ";" lvalue "${lvalue}") - string(REPLACE "[" ";" lvalue "${lvalue}") - string(REPLACE "]" "" lvalue "${lvalue}") - - - # string(REGEX REPLACE "") - message("lvalue transformed: ${lvalue}") - - set(current_scope ${scope}) - set(last_scope) - set(path ${lvalue}) - set(current_index) - set(last_index) - while(true) - set(next_scope) - set(last_index ${current_index}) - list_pop_front( path ) - ans(current_index) - list_isempty(path) - ans(is_done) - - - map_isvalid(${current_scope}) - ans(is_map) - - ref_isvalid(${current_scope}) - ans(is_ref) - - - expr_integer_isvalid("${current_index}") - ans(is_int_index) - - message("current index: ${current_index}") - message("rest:${path}") - message("int index:${is_int_index}") - message("is_done ${is_done}") - message("is_map ${is_map}") - message("is_ref ${is_ref}\n") - - - - if(is_map) - if(is_int_index) - map_keys(${current_scope} ) - ans(keys) - list_get(keys "${current_index}") - ans(current_index) - endif() - if(NOT current_index) - # invalid key - message(FATAL_ERROR "invalid key '${current_index}'") - return() - endif() - # index now is a string index in all cases - - if(is_done) - map_set(${current_scope} "${current_index}" "${rvalue}") - return_ref(rvalue) - # finished setting value - endif() - # navigate - map_tryget(${current_scope} "${current_index}") - ans(next_scope) - if(NOT next_scope) - map_new() - ans(next_scope) - map_set(${current_scope} "${current_index}" ${next_scope}) - endif() - - # next_scope exists - - elseif(is_ref) - if(is_done) - ref_set(${current_scope} ${rvalue}) - return_ref(rvalue) - # finished setting value - endif() - - if(NOT is_int_index) - message(FATAL_ERROR "can only set string indices on maps") - return() - endif() - - message(FATAL_ERROR "not iplemented for ref currently") - - - else() - message("just a var") - if(is_done) - if(NOT is_int_index) - message(FATAL_ERROR "cannot set string index for a cmake list") - endif() - map_get(${last_scope} ${last_index}) - ans(last_value) - list_set_at(last_value ${current_index} "${rvalue}") - ans(success) - if(NOT success) - message(FATAL_ERROR "cannot set ${current_index} because it is invalid for list") - endif() - map_set(${last_scope} ${last_index} "${last_value}") - endif() - endif() - - - set(last_scope ${current_scope}) - set(current_scope ${next_scope}) - - if(is_done) - break() - endif() - endwhile() - - return() - endfunction() \ No newline at end of file diff --git a/cmake/expr/expr_assignment.cmake b/cmake/expr/expr_assignment.cmake deleted file mode 100644 index 48c580c6..00000000 --- a/cmake/expr/expr_assignment.cmake +++ /dev/null @@ -1,37 +0,0 @@ - - - function(expr_assignment str scope) - set(regex_single_quote_string "'[^']*'") - set(regex_double_quote_string "\"[^\"]*\"") - set(regex_string "((${regex_single_quote_string})|(${regex_double_quote_string}))") - - string(REGEX MATCHALL "(${regex_string}|[^=]+|=)" matches "${str}") - - set(lvalues) - set(rvalues) - - foreach(match ${matches}) - string(STRIP "${match}" match) - if("${match}" STREQUAL "=" ) - list(APPEND lvalues ${rvalues}) - set(rvalues) - endif() - list(APPEND rvalues "${match}") - endforeach() - - list(REMOVE_ITEM lvalues =) - list(REMOVE_ITEM rvalues =) - - expr("${rvalues}") - - ans(rvalue) - - message("lvalues ${lvalues}") - message("rvalues ${rvalues} => ${rvalue}") - - - foreach(lvalue ${lvalues}) - expr_assign_lvalue("${lvalue}" "${rvalue}" ${scope}) - endforeach() - - endfunction() \ No newline at end of file diff --git a/cmake/expr/expr_assignment_isvalid.cmake b/cmake/expr/expr_assignment_isvalid.cmake deleted file mode 100644 index 34685866..00000000 --- a/cmake/expr/expr_assignment_isvalid.cmake +++ /dev/null @@ -1,11 +0,0 @@ -function(expr_assignment_isvalid str) - set(regex_single_quote_string "'[^']*'") - set(regex_double_quote_string "\"[^\"]*\"") - set(regex_string "((${regex_single_quote_string})|(${regex_double_quote_string}))") - - string(REGEX REPLACE "${regex_string}|[^=]" "" res "${str}") - if(res) - return(true) - endif() - return(false) - endfunction() \ No newline at end of file diff --git a/cmake/expr/expr_call.cmake b/cmake/expr/expr_call.cmake deleted file mode 100644 index 4907490a..00000000 --- a/cmake/expr/expr_call.cmake +++ /dev/null @@ -1,44 +0,0 @@ - - function(expr_call str) - string(REPLACE ";" "†" str "${str}") - - string_nested_split("${str}" "\(" "\)") - ans(parts) -#message("parts ${parts}") - foreach(part ${parts}) - set(last_part "${part}") - # message("${part}") - endforeach() - list_get(parts -2) - ans(caller) - # message("caller ${caller}") - string_slice("${caller}" 1 -2) - - ans(arguments) - string(REPLACE "†" ";" arguments "${arguments}") - set(evaluated_arguments) - foreach(argument ${arguments}) - expr("${argument}") - ans(evaluated_argument) - set(evaluated_arguments "${evaluated_arguments}†${evaluated_argument}‡") - - endforeach() - - string_remove_ending("${str}" "${caller}") - ans(path) - # message("path ${path}") - expr("${path}") - ans(evaluated_path) - - string(REPLACE "‡†" "\" \"" evaluated_arguments "${evaluated_arguments}") - string(REPLACE "‡" "\"" evaluated_arguments "${evaluated_arguments}") - string(REPLACE "†" "\"" evaluated_arguments "${evaluated_arguments}") - #message("evaled path ${evaluated_path}") - # message("args ${arguments} -> ${evaluated_arguments}") - - set(call_statement "${evaluated_path}(${evaluated_arguments})") - # message("${call_statement}") - set_ans("") - eval("${call_statement}") - return_ans() - endfunction() \ No newline at end of file diff --git a/cmake/expr/expr_call_isvalid.cmake b/cmake/expr/expr_call_isvalid.cmake deleted file mode 100644 index 3d89f69f..00000000 --- a/cmake/expr/expr_call_isvalid.cmake +++ /dev/null @@ -1,8 +0,0 @@ - - function(expr_call_isvalid str) - - if("${str}" MATCHES "^.*\\(.*\\)$") - return(true) - endif() - return(false) - endfunction() \ No newline at end of file diff --git a/cmake/expr/expr_function.cmake b/cmake/expr/expr_function.cmake deleted file mode 100644 index 2bae2fbe..00000000 --- a/cmake/expr/expr_function.cmake +++ /dev/null @@ -1,24 +0,0 @@ - - function(expr_function str) - if(COMMAND "${str}") - return_ref(str) - endif() - - is_function(isfunc "${str}") - if(isfunc) - function_new(trash) - ans(func) - function_import("${str}" as "${func}") - return_ref(func) - endif() - - lambda_isvalid("${str}") - ans(is_lambda) - if(is_lambda) - function_new(trash) - ans(func) - lambda_import("${str}" "${func}") - return_ref(func) - endif() - return() - endfunction() \ No newline at end of file diff --git a/cmake/expr/expr_function_isvalid.cmake b/cmake/expr/expr_function_isvalid.cmake deleted file mode 100644 index f0fb9b08..00000000 --- a/cmake/expr/expr_function_isvalid.cmake +++ /dev/null @@ -1,16 +0,0 @@ - - function(expr_function_isvalid str) - if(COMMAND "${str}") - return(true) - endif() - is_function(is_function "${str}") - if(is_function) - return(true) - endif() - lambda_isvalid("${str}") - ans(is_lambda) - if(is_lambda) - return(true) - endif() - return(false) - endfunction() \ No newline at end of file diff --git a/cmake/expr/expr_indexer.cmake b/cmake/expr/expr_indexer.cmake deleted file mode 100644 index 40bd5532..00000000 --- a/cmake/expr/expr_indexer.cmake +++ /dev/null @@ -1,76 +0,0 @@ - -function(expr_indexer path) - #message("indexer and stuff ${path}") - string_nested_split("${path}" "[" "]") - ans(parts) - # message("parts ${parts}") - list_get("parts" "-2") - ans(indexer) - # message("got indexer :${indexer}") -#return() - string_remove_ending("${path}" "${indexer}") - ans(path) - -# message("indexer ${indexer}") - #message("${path}") - - expr("${path}") - ans(data) - - string_slice("${indexer}" 1 -2) - ans(index_expr) - - expr("${index_expr}") - ans(index) - - # message("data: ${data}") - # message("index_expr: ${index_expr}") - # message("index :${index}") - - #integer indexation - expr_integer_isvalid("${index}") - ans(is_int) - if(is_int) - # expect data to be a list or a map - map_isvalid("${data}" ) - ans(ismap) - if(ismap) - map_keys(${data} ) - ans(keys) - list_get(keys "${index}") - ans(key) - map_get(${data} ${key}) - ans(res) - return_ref(res) - endif() - ref_isvalid("${data}") - ans(isref) - if(isref) - #deref ref - ref_get(${data}) - ans(data) - endif() - # data is list - list_get(data "${index}") - return_ans() - endif() - - # string indexation needs map - map_isvalid("${data}" ) - ans(ismap) - if(NOT ismap) - return() - endif() - - #return nothing if map does not have key - map_has(${data} "${index}") - ans(haskey) - if(NOT haskey) - return() - endif() - - map_get(${data} ${index} ) - ans(result) - return_ref(result) - -endfunction() \ No newline at end of file diff --git a/cmake/expr/expr_indexer_isvalid.cmake b/cmake/expr/expr_indexer_isvalid.cmake deleted file mode 100644 index 561f3cac..00000000 --- a/cmake/expr/expr_indexer_isvalid.cmake +++ /dev/null @@ -1,9 +0,0 @@ - - - function(expr_indexer_isvalid path) - set(regex_indexer_expr ".*\\[.+\\]") - if("${path}" MATCHES "^${regex_indexer_expr}$") - return(true) - endif() - return(false) - endfunction() \ No newline at end of file diff --git a/cmake/expr/expr_integer_isvalid.cmake b/cmake/expr/expr_integer_isvalid.cmake deleted file mode 100644 index ed535534..00000000 --- a/cmake/expr/expr_integer_isvalid.cmake +++ /dev/null @@ -1,8 +0,0 @@ - - function(expr_integer_isvalid str) - set(regex_integer "-?(0|([1-9][0-9]*))") - if("${str}" MATCHES "^${regex_integer}$") - return(true) - endif() - return(false) - endfunction() diff --git a/cmake/expr/expr_navigate.cmake b/cmake/expr/expr_navigate.cmake deleted file mode 100644 index ca586fda..00000000 --- a/cmake/expr/expr_navigate.cmake +++ /dev/null @@ -1,16 +0,0 @@ - - function(expr_navigate path) - string_split_at_last(path nav "${path}" ".") - # message("expr_nav path: ${path}, nav ${nav}") - expr("${path}") - ans(res) - map_isvalid("${res}" ) - ans(ismap) - if(NOT ismap) - return() - endif() - - map_get(${res} "${nav}") - ans(res) - return_ref(res) - endfunction() \ No newline at end of file diff --git a/cmake/expr/expr_navigate_isvalid.cmake b/cmake/expr/expr_navigate_isvalid.cmake deleted file mode 100644 index 8aa0ebd0..00000000 --- a/cmake/expr/expr_navigate_isvalid.cmake +++ /dev/null @@ -1,8 +0,0 @@ -function(expr_navigate_isvalid path) - set(regex_identifier "[a-zA-Z0-9-_]+") - set(regex_navigation_expr ".*\\.${regex_identifier}") - if("${path}" MATCHES "^${regex_navigation_expr}$") - return(true) - endif() - return(false) -endfunction() \ No newline at end of file diff --git a/cmake/expr/expr_string_isvalid.cmake b/cmake/expr/expr_string_isvalid.cmake deleted file mode 100644 index 110bdfa3..00000000 --- a/cmake/expr/expr_string_isvalid.cmake +++ /dev/null @@ -1,14 +0,0 @@ - -function(expr_string_isvalid str) - - set(regex_single_quote_string "'[^']*'") - set(regex_double_quote_string "\"[^\"]*\"") - set(regex_string "(${regex_single_quote_string}|${regex_double_quote_string})") - if("${str}" MATCHES "^${regex_single_quote_string}$") - return(true) - endif() - if("${str}" MATCHES "^(${regex_double_quote_string})$") - return(true) - endif() - return(false) -endfunction() \ No newline at end of file diff --git a/cmake/expr/expr_string_parse.cmake b/cmake/expr/expr_string_parse.cmake deleted file mode 100644 index ee1fea5f..00000000 --- a/cmake/expr/expr_string_parse.cmake +++ /dev/null @@ -1,13 +0,0 @@ - function(expr_string_parse str) - set(regex_single_quote_string "'[^']*'") - set(regex_double_quote_string "\"[^\"]*\"") - if("${str}" MATCHES "^${regex_single_quote_string}$") - string_slice("${str}" 1 -2) - return_ans() - endif() - if("${str}" MATCHES "^(${regex_double_quote_string})$") - string_slice("${str}" 1 -2) - return_ans() - endif() - return() -endfunction() \ No newline at end of file diff --git a/cmake/file/file_configure.cmake b/cmake/file/file_configure.cmake deleted file mode 100644 index 89ccab80..00000000 --- a/cmake/file/file_configure.cmake +++ /dev/null @@ -1,8 +0,0 @@ - - function(file_configure source_file target_file syntax) - if("${syntax}" STREQUAL "@-syntax") - configure_file("${source_file}" "${target_file}" @ONLY) - return() - endif() - message(FATAL_ERROR "file_configure currently only implemented @-syntax") - endfunction() \ No newline at end of file diff --git a/cmake/file/file_configure_write.cmake b/cmake/file/file_configure_write.cmake deleted file mode 100644 index 4643620b..00000000 --- a/cmake/file/file_configure_write.cmake +++ /dev/null @@ -1,30 +0,0 @@ - - # configures a file and path - function(file_configure_write base_dir file_name content) - if(EXISTS "${content}") - set(source_file "${content}") - set(temp_file false) - else() - file_make_temporary( "${content}") - ans(source_file) - set(temp_file true) - endif() - - # configure relative file path - map_format("${file_name}") - ans(configured_path) - - # if file exists configure it - if(EXISTS "${source_file}" AND NOT IS_DIRECTORY "${source_file}") - file_configure("${source_file}" "${base_dir}/${configured_path}" "@-syntax") - endif() - - # remove temporary file - if(temp_file) - # file(REMOVE "${source_file}") - endif() - return("${base_dir}/${configured_path}") - endfunction() - - - \ No newline at end of file diff --git a/cmake/file/file_configure_write_map.cmake b/cmake/file/file_configure_write_map.cmake deleted file mode 100644 index 428abd85..00000000 --- a/cmake/file/file_configure_write_map.cmake +++ /dev/null @@ -1,20 +0,0 @@ - - # configures a map of files where the key is the configurable path and content - # is either an existing file or string cotnent for a file - function(file_configure_write_map base_dir files) - obj("${files}") - ans(files) - set(res) - map_keys("${files}" ) - ans(file_names) - foreach(file_name ${file_names}) - # get value for file. if value is a valid file use that file - # else use value as the content for the file - map_get("${files}" "${file_name}") - ans(content) - file_configure_write("${base_dir}" "${file_name}" "${content}") - ans(configured_path) - list(APPEND res "${configured_path}") - endforeach() - return(${res}) - endfunction() \ No newline at end of file diff --git a/cmake/file/file_equals.cmake b/cmake/file/file_equals.cmake deleted file mode 100644 index 2a3c56b1..00000000 --- a/cmake/file/file_equals.cmake +++ /dev/null @@ -1,19 +0,0 @@ - ## compares the specified files - ## returning true if their content is the same else false - function(file_equals lhs rhs) - path("${lhs}") - ans(lhs) - - path("${rhs}") - ans(rhs) - - cmake(-E compare_files "${lhs}" "${rhs}" --return-code) - ans(return_code) - - if("${return_code}" STREQUAL "0") - return(true) - else() - return(false) - endif() - - endfunction() diff --git a/cmake/file/file_extendend_glob.cmake b/cmake/file/file_extendend_glob.cmake deleted file mode 100644 index ef1ad894..00000000 --- a/cmake/file/file_extendend_glob.cmake +++ /dev/null @@ -1,37 +0,0 @@ - - -# adds additional syntax to glob allowing exclusion by prepending a ! exclamation mark. -# e.g. -# file_extended_glob("dir" "**.cpp" "!build/**") -# returns all cpp files in dir except if they are in the dir/build directory -function(file_extended_glob base_dir) - set(args ${ARGN}) - list_extract_flag(args --relative) - ans(relative) - if(relative) - set(relative --relative) - endif() - set(includes) - set(excludes) - foreach(current ${args}) - if("${current}" MATCHES "!.*") - string(SUBSTRING "${current}" "1" "-1" current) - list(APPEND excludes "${current}") - else() - list(APPEND includes "${current}") - endif() - endforeach() - - file_glob("${base_dir}" ${includes} ${relative}) - ans(includes) - - file_glob("${base_dir}" ${excludes} ${relative}) - ans(excludes) - - - if(includes AND excludes) - list(REMOVE_ITEM includes ${excludes}) - endif() - - return_ref(includes) -endfunction() \ No newline at end of file diff --git a/cmake/file/file_find.cmake b/cmake/file/file_find.cmake deleted file mode 100644 index 26af75da..00000000 --- a/cmake/file/file_find.cmake +++ /dev/null @@ -1,29 +0,0 @@ -function(file_find file include_dirs extensions) - #message("trying to find ${file} in ${include_dirs}") - set(extensions "false;${extensions}") - set(include_dirs "false;${include_dirs}") - foreach(dir ${include_dirs}) - if(dir AND NOT "${dir}" MATCHES ".*/$") - set(dir "${dir}/") - elseif(NOT dir) - set(dir "") - endif() - - foreach(extension ${extensions}) - if(extension AND NOT "${extension}" MATCHES "^\\..*" ) - set(extension ".${extension}") - elseif(NOT extension) - set(extension "") - endif() - set(test_path "${dir}${file}${extension}") - #message("test_path ${test_path}") - string(STRIP "${test_path}" test_path) - if(EXISTS "${test_path}" ) - get_filename_component(file "${test_path}" REALPATH) - #message("returning ${file}") - return_ref(file) - endif() - endforeach() - endforeach() - return() - endfunction() \ No newline at end of file diff --git a/cmake/file/file_find_parent_of_up.cmake b/cmake/file/file_find_parent_of_up.cmake deleted file mode 100644 index b7a388c2..00000000 --- a/cmake/file/file_find_parent_of_up.cmake +++ /dev/null @@ -1,10 +0,0 @@ - -function(file_find_up_parent path n target) - file_find_up("${path}" "${n}" "${target}") - ans(res) - if(NOT res) - return() - endif() - path_component("${res}" --parent-dir) - return_ans() -endfunction() \ No newline at end of file diff --git a/cmake/file/file_find_up.cmake b/cmake/file/file_find_up.cmake deleted file mode 100644 index e3252a36..00000000 --- a/cmake/file/file_find_up.cmake +++ /dev/null @@ -1,23 +0,0 @@ - - -# returns the first match in parent dir or parent dirs of path -function(file_find_up path n target) - - path("${path}") - ans(path) - - - # /tld is appended because only its parent dirs are gotten - path_parent_dirs("${path}/tld" ${n}) - ans(parent_dirs) - - foreach(parent_dir ${parent_dirs}) - if(IS_DIRECTORY "${parent_dir}/${target}") - return("${parent_dir}/${target}") - endif() - endforeach() - return() - -endfunction() - - diff --git a/cmake/file/file_glob.cmake b/cmake/file/file_glob.cmake deleted file mode 100644 index fbceb644..00000000 --- a/cmake/file/file_glob.cmake +++ /dev/null @@ -1,61 +0,0 @@ -# an extended glob function -# globs paths relative to base_dir -# glob expressions starting with / will be normally globbed -# expressions not starting with slash will be globbed recrusively -# specifiying the --relative flag will return paths relative to base_dir -function(file_glob base_dir) - set(args ${ARGN}) - list_extract_flag(args --relative) - ans(relative) - - set(globs) - set(globs_recurse) - foreach(arg ${args}) - string_starts_with("${arg}" /) - ans(notRecurse) - if(notRecurse) - string(SUBSTRING "${arg}" 1 -1 arg) - list(APPEND globs ${arg}) - else() - list(APPEND globs_recurse ${arg}) - endif() - endforeach() - set(slash /) - - list_combinations(base_dir slash globs) - ans(globs) - - list_combinations(base_dir slash globs_recurse) - ans(globs_recurse) - - - set(glob_files) - set(glob_recurse_files) - if(globs) - file(GLOB glob_files ${globs}) - endif() - if(globs_recurse) - file(GLOB_RECURSE glob_recurse_files ${globs_recurse}) - endif() - - set(files) - - foreach(file ${glob_files} ${glob_recurse_files}) - get_filename_component(file "${file}" REALPATH) - if(relative) - file(RELATIVE_PATH file ${base_dir} ${file}) - list(APPEND files "${file}") - else() - list(APPEND files "${file}") - endif() - endforeach() - if(files) - list(REMOVE_DUPLICATES files) - endif() - # foreach(file ${files}) - # message("file ${file}") - # endforeach() - - - return_ref(files) -endfunction() diff --git a/cmake/file/file_issubdirectoryof.cmake b/cmake/file/file_issubdirectoryof.cmake deleted file mode 100644 index e21f8a2f..00000000 --- a/cmake/file/file_issubdirectoryof.cmake +++ /dev/null @@ -1,7 +0,0 @@ - - function(file_issubdirectoryof subdir path) - get_filename_component(path "${path}" REALPATH) - get_filename_component(subdir "${subdir}" REALPATH) - string_starts_with("${subdir}" "${path}") - return_ans() - endfunction() \ No newline at end of file diff --git a/cmake/file/file_make_temporary.cmake b/cmake/file/file_make_temporary.cmake deleted file mode 100644 index 717bfc11..00000000 --- a/cmake/file/file_make_temporary.cmake +++ /dev/null @@ -1,11 +0,0 @@ -# creates a temporary file -function(file_make_temporary content) - oocmake_config(temp_dir) - ans(temp_dir) - file_random( "${temp_dir}/file_make_temporary_{{id}}.tmp") - ans(rnd) - file(WRITE ${rnd} "${content}") - return_ref(rnd) -endfunction() - - diff --git a/cmake/file/file_tempdir.cmake b/cmake/file/file_tempdir.cmake deleted file mode 100644 index 44152e99..00000000 --- a/cmake/file/file_tempdir.cmake +++ /dev/null @@ -1,19 +0,0 @@ -# creates a temporary directory and returns its path -function(file_tempdir ) - oocmake_config(temp_dir) - ans(temp_dir) - #string(MAKE_C_IDENTIFIER id "${ARGN}") - string_normalize( "${ARGN}") - ans(id) - set(tempdir "${temp_dir}/file_tempdir/${id}") - set(i 0) - while(true) - if(NOT EXISTS "${tempdir}_${i}") - set(tmp_dir "${tempdir}_${i}" PARENT_SCOPE) - - file(MAKE_DIRECTORY "${tempdir}_${i}") - return("${tempdir}_${i}") - endif() - math(EXPR i "${i} + 1") - endwhile() -endfunction() \ No newline at end of file diff --git a/cmake/file/file_write.cmake b/cmake/file/file_write.cmake deleted file mode 100644 index 13f9c02b..00000000 --- a/cmake/file/file_write.cmake +++ /dev/null @@ -1,12 +0,0 @@ - - # writes the specified content to the specified path - function(file_write path content) - path("${path}") - ans(path) - if(IS_DIRECTORY "${path}") - message(WARNING "trying to write to path '${path}' which is a directory") - return(false) - endif() - file(WRITE "${path}" "${content}") - return(true) - endfunction() \ No newline at end of file diff --git a/cmake/file/path_find_first_parent_dir_containing.cmake b/cmake/file/path_find_first_parent_dir_containing.cmake deleted file mode 100644 index dd35844e..00000000 --- a/cmake/file/path_find_first_parent_dir_containing.cmake +++ /dev/null @@ -1,19 +0,0 @@ - - # finds the closest parent dir (or dir itself) - # that contains any of the specified glob expressions - # (also see file_glob for syntax) - function(path_find_first_parent_dir_containing dir ) - file_glob_up("${dir}" 0 ${ARGN}) - ans(matches) - list_peek_front(matches) - ans(first_match) - if(NOT first_match) - return() - endif() - - - path_component("${first_match}" PATH) - ans(first_match) - - return_ref(first_match) - endfunction() \ No newline at end of file diff --git a/cmake/filesystem/README.md b/cmake/filesystem/README.md index 26c5567f..7a47c8cc 100644 --- a/cmake/filesystem/README.md +++ b/cmake/filesystem/README.md @@ -76,7 +76,7 @@ popd() # pwd is now ${CMAKE_SOURCE_DIR} again and stack is empty - `fappend( )->void` appends the specified content to the target file - `fwrite( )->void` writes the content to the target file (overwriting it) - `parent_dir()->` returns the parent directory of the specified path - - `file_timestamp()->` returns the timestamp string for the specified path yyyy-MM-ddThh:mm:ss + - `ftime()->` returns the timestamp string for the specified path yyyy-MM-ddThh:mm:ss - `ls([])->[]` returns files and subfolders of specified path - `mkdir()->` creates the specified dir and returns its qualified path - `mkdirs(...)->[]` creates all of the directories specified diff --git a/cmake/filesystem/README.md.in b/cmake/filesystem/README.md.in index 4f7eee10..5165183f 100644 --- a/cmake/filesystem/README.md.in +++ b/cmake/filesystem/README.md.in @@ -76,7 +76,7 @@ popd() # pwd is now ${CMAKE_SOURCE_DIR} again and stack is empty - `fappend( )->void` appends the specified content to the target file - `fwrite( )->void` writes the content to the target file (overwriting it) - `parent_dir()->` returns the parent directory of the specified path - - `file_timestamp()->` returns the timestamp string for the specified path yyyy-MM-ddThh:mm:ss + - `ftime()->` returns the timestamp string for the specified path yyyy-MM-ddThh:mm:ss - `ls([])->[]` returns files and subfolders of specified path - `mkdir()->` creates the specified dir and returns its qualified path - `mkdirs(...)->[]` creates all of the directories specified diff --git a/cmake/file/archive_isvalid.cmake b/cmake/filesystem/archive/archive_isvalid.cmake similarity index 100% rename from cmake/file/archive_isvalid.cmake rename to cmake/filesystem/archive/archive_isvalid.cmake diff --git a/cmake/file/archive_ls.cmake b/cmake/filesystem/archive/archive_ls.cmake similarity index 69% rename from cmake/file/archive_ls.cmake rename to cmake/filesystem/archive/archive_ls.cmake index f7dea26e..bad57ac7 100644 --- a/cmake/file/archive_ls.cmake +++ b/cmake/filesystem/archive/archive_ls.cmake @@ -1,3 +1,4 @@ + function(archive_ls archive) path_qualify(archive) @@ -7,13 +8,18 @@ function(archive_ls archive) if("${types}" MATCHES "application/x-gzip") + checksum_file("${archive}") + ans(key) + string_cache_return_hit(archive_ls_cache "${key}") + + tar(tf "${archive}") ans(files) - tar(tf "${archive}" --result) + tar(tf "${archive}" --process-handle) ans(result) - assign(error = result.error) + assign(error = result.exit_code) if(error) error("tar exited with {result.error}") return() @@ -23,6 +29,8 @@ function(archive_ls archive) string(REGEX MATCHALL "(^|\n)([^\n]+)(\n|$)" files "${files}") string(REGEX REPLACE "(\r|\n)" "" files "${files}") + + string_cache_update(archive_ls_cache "${key}" "${files}") return_ref(files) else() diff --git a/cmake/file/archive_match_files.cmake b/cmake/filesystem/archive/archive_match_files.cmake similarity index 72% rename from cmake/file/archive_match_files.cmake rename to cmake/filesystem/archive/archive_match_files.cmake index 90f3948b..7e34dd47 100644 --- a/cmake/file/archive_match_files.cmake +++ b/cmake/filesystem/archive/archive_match_files.cmake @@ -1,4 +1,5 @@ - +## returns all files which match the specified regex +## the regex must match the whole filename function(archive_match_files archive regex) set(args ${ARGN}) @@ -17,11 +18,8 @@ function(archive_match_files archive regex) archive_ls("${archive}") ans(files) - - list_regex_match(files "${regex}") - ans(files) - - + string(REGEX MATCHALL "(^|;)(${regex})(;|$)" files "${files}") + set(files ${files}) # necessary because of leading and trailing ; else() message(FATAL_ERROR "${archive} unsupported compression: '${types}'") endif() @@ -38,6 +36,5 @@ function(archive_match_files archive regex) ans(files) endif() - return_ref(files) endfunction() \ No newline at end of file diff --git a/cmake/file/archive_read_file.cmake b/cmake/filesystem/archive/archive_read_file.cmake similarity index 93% rename from cmake/file/archive_read_file.cmake rename to cmake/filesystem/archive/archive_read_file.cmake index 77a8f4b8..3039bdfc 100644 --- a/cmake/file/archive_read_file.cmake +++ b/cmake/filesystem/archive/archive_read_file.cmake @@ -2,7 +2,7 @@ function(archive_read_file archive file) path_qualify(archive) - file_tempdir() + mktemp() ans(temp_dir) uncompress_file("${temp_dir}" "${archive}" "${file}") fread("${temp_dir}/${file}") diff --git a/cmake/file/archive_read_file_match.cmake b/cmake/filesystem/archive/archive_read_file_match.cmake similarity index 100% rename from cmake/file/archive_read_file_match.cmake rename to cmake/filesystem/archive/archive_read_file_match.cmake diff --git a/cmake/file/compress.cmake b/cmake/filesystem/archive/compress.cmake similarity index 100% rename from cmake/file/compress.cmake rename to cmake/filesystem/archive/compress.cmake diff --git a/cmake/file/compress_tgz.cmake b/cmake/filesystem/archive/compress_tgz.cmake similarity index 74% rename from cmake/file/compress_tgz.cmake rename to cmake/filesystem/archive/compress_tgz.cmake index 108e197d..ff8a8a5c 100644 --- a/cmake/file/compress_tgz.cmake +++ b/cmake/filesystem/archive/compress_tgz.cmake @@ -1,14 +1,11 @@ function(compress_tgz target_file) + set(args ${ARGN}) # target_file file path_qualify(target_file) - # get current working dir - pwd() - ans(pwd) - # get all files to compress - file_glob("${pwd}" ${args} --relative) + glob(${args} --relative) ans(paths) # compress all files into target_file using paths relative to pwd() diff --git a/cmake/file/file_istarfile.cmake b/cmake/filesystem/archive/file_istarfile.cmake similarity index 89% rename from cmake/file/file_istarfile.cmake rename to cmake/filesystem/archive/file_istarfile.cmake index 79ecd693..995cfcbb 100644 --- a/cmake/file/file_istarfile.cmake +++ b/cmake/filesystem/archive/file_istarfile.cmake @@ -7,7 +7,7 @@ function(file_istarfile file) if(IS_DIRECTORY "${file}") return(false) endif() - tar(ztvf "${file}" --return-code) + tar(ztvf "${file}" --exit-code) ans(res) if(NOT res EQUAL 0) return(false) diff --git a/cmake/file/tar.cmake b/cmake/filesystem/archive/tar.cmake similarity index 96% rename from cmake/file/tar.cmake rename to cmake/filesystem/archive/tar.cmake index 357d44ef..016c797a 100644 --- a/cmake/file/tar.cmake +++ b/cmake/filesystem/archive/tar.cmake @@ -1,7 +1,7 @@ -# tar command -# use cvzf to compress files relative to pwd() to a tgz file -# use xzf to uncompress a tgz file to the pwd() -function(tar) - cmake(-E tar ${ARGN}) - return_ans() +# tar command +# use cvzf to compress files relative to pwd() to a tgz file +# use xzf to uncompress a tgz file to the pwd() +function(tar) + cmake(-E tar ${ARGN}) + return_ans() endfunction() \ No newline at end of file diff --git a/cmake/file/uncompress.cmake b/cmake/filesystem/archive/uncompress.cmake similarity index 90% rename from cmake/file/uncompress.cmake rename to cmake/filesystem/archive/uncompress.cmake index 90d85cbe..72f82998 100644 --- a/cmake/file/uncompress.cmake +++ b/cmake/filesystem/archive/uncompress.cmake @@ -4,7 +4,7 @@ function(uncompress file) ans(types) if("${types}" MATCHES "application/x-gzip") - directory_ensure_exists(".") + dir_ensure_exists(".") path_qualify(file) tar(xzf "${file}" ${ARGN}) return_ans() diff --git a/cmake/file/uncompress_file.cmake b/cmake/filesystem/archive/uncompress_file.cmake similarity index 100% rename from cmake/file/uncompress_file.cmake rename to cmake/filesystem/archive/uncompress_file.cmake diff --git a/cmake/filesystem/directory_ensure_exists.cmake b/cmake/filesystem/dir_ensure_exists.cmake similarity index 87% rename from cmake/filesystem/directory_ensure_exists.cmake rename to cmake/filesystem/dir_ensure_exists.cmake index 99ac9c71..6287c627 100644 --- a/cmake/filesystem/directory_ensure_exists.cmake +++ b/cmake/filesystem/dir_ensure_exists.cmake @@ -1,7 +1,7 @@ ## ensures that the directory specified exists ## the directory is qualified with path() -function(directory_ensure_exists path) +function(dir_ensure_exists path) path("${path}") ans(path) if(EXISTS "${path}") diff --git a/cmake/file/dir_isempty.cmake b/cmake/filesystem/dir_isempty.cmake similarity index 100% rename from cmake/file/dir_isempty.cmake rename to cmake/filesystem/dir_isempty.cmake diff --git a/cmake/filesystem/fappend.cmake b/cmake/filesystem/file/fappend.cmake similarity index 95% rename from cmake/filesystem/fappend.cmake rename to cmake/filesystem/file/fappend.cmake index d63ea0dd..526016e8 100644 --- a/cmake/filesystem/fappend.cmake +++ b/cmake/filesystem/file/fappend.cmake @@ -1,6 +1,6 @@ -function(fappend path) - path("${path}") - ans(path) - file(APPEND "${path}" ${ARGN}) - return() +function(fappend path) + path("${path}") + ans(path) + file(APPEND "${path}" ${ARGN}) + return() endfunction() \ No newline at end of file diff --git a/cmake/filesystem/file/fequal.cmake b/cmake/filesystem/file/fequal.cmake new file mode 100644 index 00000000..41fb404b --- /dev/null +++ b/cmake/filesystem/file/fequal.cmake @@ -0,0 +1,14 @@ + ## compares the specified files + ## returning true if their content is the same else false + function(fequal lhs rhs) + path_qualify(lhs) + path_qualify(rhs) + + cmake(-E compare_files "${lhs}" "${rhs}" --exit-code) + ans(error) + + if(error) + return(false) + endif() + return(true) + endfunction() diff --git a/cmake/filesystem/file/fprint.cmake b/cmake/filesystem/file/fprint.cmake new file mode 100644 index 00000000..1cd97c32 --- /dev/null +++ b/cmake/filesystem/file/fprint.cmake @@ -0,0 +1,16 @@ +## prints the specified file to the console +function(fprint path) + fread("${path}") + ans(res) + _message("${res}") + return() +endfunction() + + +function(fprint_try path) + path_qualify(path) + if(EXISTS "${path}") + fprint("${path}") + endif() + returN() +endfunction() \ No newline at end of file diff --git a/cmake/filesystem/fread.cmake b/cmake/filesystem/file/fread.cmake similarity index 96% rename from cmake/filesystem/fread.cmake rename to cmake/filesystem/file/fread.cmake index 43c64d69..845159e4 100644 --- a/cmake/filesystem/fread.cmake +++ b/cmake/filesystem/file/fread.cmake @@ -1,7 +1,7 @@ -# reads the file specified and returns its content -function(fread path) - path("${path}") - ans(path) - file(READ "${path}" res) - return_ref(res) +# reads the file specified and returns its content +function(fread path) + path("${path}") + ans(path) + file(READ "${path}" res) + return_ref(res) endfunction() \ No newline at end of file diff --git a/cmake/file/fread_data.cmake b/cmake/filesystem/file/fread_data.cmake similarity index 100% rename from cmake/file/fread_data.cmake rename to cmake/filesystem/file/fread_data.cmake diff --git a/cmake/filesystem/fread_lines.cmake b/cmake/filesystem/file/fread_lines.cmake similarity index 100% rename from cmake/filesystem/fread_lines.cmake rename to cmake/filesystem/file/fread_lines.cmake diff --git a/cmake/filesystem/fread_unicode16.cmake b/cmake/filesystem/file/fread_unicode16.cmake similarity index 100% rename from cmake/filesystem/fread_unicode16.cmake rename to cmake/filesystem/file/fread_unicode16.cmake diff --git a/cmake/filesystem/file_timestamp.cmake b/cmake/filesystem/file/ftime.cmake similarity index 73% rename from cmake/filesystem/file_timestamp.cmake rename to cmake/filesystem/file/ftime.cmake index e237fb5d..8e12f5a7 100644 --- a/cmake/filesystem/file_timestamp.cmake +++ b/cmake/filesystem/file/ftime.cmake @@ -1,8 +1,7 @@ ## returns the timestamp for the specified path -function(file_timestamp path) - path("${path}") - ans(path) +function(ftime path) + path_qualify(path) if(NOT EXISTS "${path}") return() @@ -10,6 +9,5 @@ function(file_timestamp path) file(TIMESTAMP "${path}" res) - return_ref(res) endfunction() \ No newline at end of file diff --git a/cmake/filesystem/fwrite.cmake b/cmake/filesystem/file/fwrite.cmake similarity index 85% rename from cmake/filesystem/fwrite.cmake rename to cmake/filesystem/file/fwrite.cmake index 7bd1c334..27807f5e 100644 --- a/cmake/filesystem/fwrite.cmake +++ b/cmake/filesystem/file/fwrite.cmake @@ -1,9 +1,8 @@ -# writs argn to the speicified file creating it if it does not exist and -# overwriting it if it does. -function(fwrite path) - path("${path}") - ans(path) - file(WRITE "${path}" "${ARGN}") - event_emit(on_fwrite "${path}") - return_ref(path) +# writs argn to the speicified file creating it if it does not exist and +# overwriting it if it does. +function(fwrite path) + path_qualify(path) + file(WRITE "${path}" "${ARGN}") + event_emit(on_fwrite "${path}") + return_ref(path) endfunction() \ No newline at end of file diff --git a/cmake/filesystem/fwrite_data.cmake b/cmake/filesystem/file/fwrite_data.cmake similarity index 100% rename from cmake/filesystem/fwrite_data.cmake rename to cmake/filesystem/file/fwrite_data.cmake diff --git a/cmake/filesystem/file_extensions.cmake b/cmake/filesystem/file_extensions.cmake deleted file mode 100644 index 09b226e9..00000000 --- a/cmake/filesystem/file_extensions.cmake +++ /dev/null @@ -1,7 +0,0 @@ -# retuns the extension of the specified file -function(file_extension path) - path("${path}") - ans(path) - get_filename_component(res "${path}" EXT) - return_ref(res) -endfunction() \ No newline at end of file diff --git a/cmake/filesystem/file_make_temporary.cmake b/cmake/filesystem/file_make_temporary.cmake new file mode 100644 index 00000000..a8d98026 --- /dev/null +++ b/cmake/filesystem/file_make_temporary.cmake @@ -0,0 +1,36 @@ +# creates a temporary file +function(file_make_temporary content) + cmakepp_config(temp_dir) + ans(temp_dir) + file_random( "${temp_dir}/file_make_temporary_{{id}}.tmp") + ans(rnd) + file(WRITE ${rnd} "${content}") + return_ref(rnd) +endfunction() + + +## +## +## creates a temporary file containing the specified content +## returns the path for that file +## --pattern +## --extension +## --dir +function(fwrite_temp content) + message(FATAL_ERROR not implemented) + set(args ${ARGN}) + list_extract_value(args --pattern) + ans(pattern) + + if(NOT pattern) + + list_extract_value(args --extension) + ans(ext) + list_extract_value(args --dir) + ans(dir) + if(NOT ext) + + endif() + +return() +endfunction() \ No newline at end of file diff --git a/cmake/filesystem/file_map_write.cmake b/cmake/filesystem/file_map_write.cmake index bad462fe..5ba9a5af 100644 --- a/cmake/filesystem/file_map_write.cmake +++ b/cmake/filesystem/file_map_write.cmake @@ -65,12 +65,9 @@ function(file_map_read) message("path ${path}") file(GLOB_RECURSE paths RELATIVE "${path}" ${path}/**) - #file_glob("${path}" **/** --relative) - #ans(paths) message("paths ${paths}") - # paths_to_map(${paths}) return_ans() diff --git a/cmake/file/file_random.cmake b/cmake/filesystem/file_random.cmake similarity index 92% rename from cmake/file/file_random.cmake rename to cmake/filesystem/file_random.cmake index 364fe982..370523bf 100644 --- a/cmake/file/file_random.cmake +++ b/cmake/filesystem/file_random.cmake @@ -2,7 +2,8 @@ # the first file which does not exist is returned function(file_random in_pattern) while(true) - make_guid(id) + make_guid() + ans(id) string(REPLACE "{{id}}" ${id} in_pattern ${in_pattern}) set(current_name "${in_pattern}") if(NOT EXISTS ${current_name}) diff --git a/cmake/shell/file_temp_name.cmake b/cmake/filesystem/file_temp_name.cmake similarity index 92% rename from cmake/shell/file_temp_name.cmake rename to cmake/filesystem/file_temp_name.cmake index 0db0eae5..729fc8a3 100644 --- a/cmake/shell/file_temp_name.cmake +++ b/cmake/filesystem/file_temp_name.cmake @@ -8,7 +8,7 @@ # id will be varied untikl a file is found which does not exist # the complete path will be returned function(file_temp_name template) - oocmake_config(temp_dir) + cmakepp_config(temp_dir) ans(temp_dir) file_random( "${temp_dir}/${template}") ans(rnd) diff --git a/cmake/file/file_tmp.cmake b/cmake/filesystem/file_tmp.cmake similarity index 87% rename from cmake/file/file_tmp.cmake rename to cmake/filesystem/file_tmp.cmake index 4e904bbe..f50aa7fc 100644 --- a/cmake/file/file_tmp.cmake +++ b/cmake/filesystem/file_tmp.cmake @@ -1,10 +1,10 @@ - -# creates a temporary file with a specific extension -function(file_tmp extension content) - oocmake_config(temp_dir) - ans(temp_dir) - file_random( "${temp_dir}/file_make_temporary_{{id}}.${extension}") - ans(rnd) - file(WRITE ${rnd} "${content}") - return_ref(rnd) -endfunction() + +# creates a temporary file with a specific extension +function(file_tmp extension content) + cmakepp_config(temp_dir) + ans(temp_dir) + file_random( "${temp_dir}/file_make_temporary_{{id}}.${extension}") + ans(rnd) + file(WRITE ${rnd} "${content}") + return_ref(rnd) +endfunction() diff --git a/cmake/filesystem/flines.cmake b/cmake/filesystem/flines.cmake deleted file mode 100644 index 40b38923..00000000 --- a/cmake/filesystem/flines.cmake +++ /dev/null @@ -1,8 +0,0 @@ -# reads the file specified and returns its content -function(flines path) - path("${path}") - ans(path) - file(STRINGS "${path}" res) - return_ref(res) -endfunction() - diff --git a/cmake/filesystem/fprint.cmake b/cmake/filesystem/fprint.cmake deleted file mode 100644 index 28bcb476..00000000 --- a/cmake/filesystem/fprint.cmake +++ /dev/null @@ -1,7 +0,0 @@ -## prints the specified file to the console -function(fprint path) - fread("${path}") - ans(res) - message("${res}") - return() -endfunction() diff --git a/cmake/filesystem/glob.cmake b/cmake/filesystem/glob.cmake deleted file mode 100644 index 85c7295c..00000000 --- a/cmake/filesystem/glob.cmake +++ /dev/null @@ -1,39 +0,0 @@ - - - - ## glob( [--relative] [--recurse]) -> - ## - ## - function(glob) - set(args ${ARGN}) - list_extract_flag(args --relative) - ans(relative) - - list_extract_flag(args --recurse) - ans(recurse) - - - glob_paths(${args}) - ans(globs) - - pwd() - ans(pwd) - if(recurse) - set(glob_command GLOB_RECURSE) - else() - set(glob_command GLOB) - endif() - - if(relative) - set(relative RELATIVE "${pwd}") - else() - set(relative) - endif() - - set(paths) - if(globs) - file(${glob_command} paths ${relative} ${globs}) - list_remove_duplicates(paths) - endif() - return_ref(paths) - endfunction() diff --git a/cmake/filesystem/globbing/glob.cmake b/cmake/filesystem/globbing/glob.cmake new file mode 100644 index 00000000..aec28e44 --- /dev/null +++ b/cmake/filesystem/globbing/glob.cmake @@ -0,0 +1,43 @@ +## `( [--relative] [--recurse]) -> |` +## +## **flags**: +## * `--relative` causes the output to be paths realtive to current `pwd()` +## * `--recurse` causes the glob expression to be applied recursively +## **scope** +## * `pwd()` influences the relative paths +## **returns** +## * list of files matching the specified glob expressions +function(glob) + set(args ${ARGN}) + list_extract_flag(args --relative) + ans(relative) + + list_extract_flag(args --recurse) + ans(recurse) + + glob_paths(${args}) + ans(globs) + + if(recurse) + set(glob_command GLOB_RECURSE) + else() + set(glob_command GLOB) + endif() + + if(relative) + pwd() + ans(pwd) + set(relative RELATIVE "${pwd}") + else() + set(relative) + endif() + + set(paths) + + if(globs) + file(${glob_command} paths ${relative} ${globs}) + list_remove_duplicates(paths) + endif() + + return_ref(paths) +endfunction() diff --git a/cmake/filesystem/glob_expression_parse.cmake b/cmake/filesystem/globbing/glob_expression_parse.cmake similarity index 100% rename from cmake/filesystem/glob_expression_parse.cmake rename to cmake/filesystem/globbing/glob_expression_parse.cmake diff --git a/cmake/filesystem/glob_ignore.cmake b/cmake/filesystem/globbing/glob_ignore.cmake similarity index 100% rename from cmake/filesystem/glob_ignore.cmake rename to cmake/filesystem/globbing/glob_ignore.cmake diff --git a/cmake/filesystem/globbing/glob_parent_dir_containing.cmake b/cmake/filesystem/globbing/glob_parent_dir_containing.cmake new file mode 100644 index 00000000..4e0b9bc3 --- /dev/null +++ b/cmake/filesystem/globbing/glob_parent_dir_containing.cmake @@ -0,0 +1,20 @@ +## `( )->` +## +## +## finds the closest parent dir (or dir itself) +## that contains any of the specified glob expressions +## (also see file_glob for syntax) +function(glob_parent_dir_containing ) + glob_up(0 ${ARGN}) + ans(matches) + list_peek_front(matches) + ans(first_match) + if(NOT first_match) + return() + endif() + + path_component("${first_match}" PATH) + ans(first_match) + + return_ref(first_match) +endfunction() \ No newline at end of file diff --git a/cmake/filesystem/glob_path.cmake b/cmake/filesystem/globbing/glob_path.cmake similarity index 100% rename from cmake/filesystem/glob_path.cmake rename to cmake/filesystem/globbing/glob_path.cmake diff --git a/cmake/filesystem/glob_paths.cmake b/cmake/filesystem/globbing/glob_paths.cmake similarity index 100% rename from cmake/filesystem/glob_paths.cmake rename to cmake/filesystem/globbing/glob_paths.cmake diff --git a/cmake/file/file_glob_up.cmake b/cmake/filesystem/globbing/glob_up.cmake similarity index 62% rename from cmake/file/file_glob_up.cmake rename to cmake/filesystem/globbing/glob_up.cmake index 3aa21be5..ca30675e 100644 --- a/cmake/file/file_glob_up.cmake +++ b/cmake/filesystem/globbing/glob_up.cmake @@ -1,20 +1,26 @@ # applies the glob expressions (passed as varargs) -# to the first n parent directories starting with the specified path +# to the first n parent directories starting with the current dir # order of result is in deepest path first # 0 searches parent paths up to root +# warning do not use --recurse and unlimited depth as it would probably take forever # @todo extend to quit search when first result is found -function(file_glob_up path n) +function(glob_up n) + set(args ${ARGN}) + + # extract dir + set(path) path("${path}") ans(path) - set(globs ${ARGN}) + + set(globs ${args}) # /tld is appended because only its parent dirs are gotten path_parent_dirs("${path}/tld" ${n}) ans(parent_dirs) - set(all_matches ) + set(all_matches) foreach(parent_dir ${parent_dirs}) - file_glob("${parent_dir}" ${globs}) + glob("${parent_dir}" ${globs}) ans(matches) list(APPEND all_matches ${matches}) endforeach() diff --git a/cmake/filesystem/ls.cmake b/cmake/filesystem/globbing/ls.cmake similarity index 60% rename from cmake/filesystem/ls.cmake rename to cmake/filesystem/globbing/ls.cmake index fb1f8fef..4a5a9642 100644 --- a/cmake/filesystem/ls.cmake +++ b/cmake/filesystem/globbing/ls.cmake @@ -1,5 +1,7 @@ -# returns a list of files -# todo: http://ss64.com/bash/ls.html +## `()-> ` +## +## returns a list of files and folders in the specified directory +## function(ls) path("${ARGN}") ans(path) @@ -11,3 +13,5 @@ function(ls) file(GLOB files "${path}") return_ref(files) endfunction() + + diff --git a/cmake/file/mime_type.cmake b/cmake/filesystem/mime_type/mime_type.cmake similarity index 100% rename from cmake/file/mime_type.cmake rename to cmake/filesystem/mime_type/mime_type.cmake diff --git a/cmake/file/mime_type_from_extension.cmake b/cmake/filesystem/mime_type/mime_type_from_extension.cmake similarity index 100% rename from cmake/file/mime_type_from_extension.cmake rename to cmake/filesystem/mime_type/mime_type_from_extension.cmake diff --git a/cmake/file/mime_type_from_filename.cmake b/cmake/filesystem/mime_type/mime_type_from_filename.cmake similarity index 100% rename from cmake/file/mime_type_from_filename.cmake rename to cmake/filesystem/mime_type/mime_type_from_filename.cmake diff --git a/cmake/file/mime_type_get.cmake b/cmake/filesystem/mime_type/mime_type_get.cmake similarity index 100% rename from cmake/file/mime_type_get.cmake rename to cmake/filesystem/mime_type/mime_type_get.cmake diff --git a/cmake/file/mime_type_get_extension.cmake b/cmake/filesystem/mime_type/mime_type_get_extension.cmake similarity index 100% rename from cmake/file/mime_type_get_extension.cmake rename to cmake/filesystem/mime_type/mime_type_get_extension.cmake diff --git a/cmake/file/mime_type_map.cmake b/cmake/filesystem/mime_type/mime_type_map.cmake similarity index 100% rename from cmake/file/mime_type_map.cmake rename to cmake/filesystem/mime_type/mime_type_map.cmake diff --git a/cmake/file/mime_type_register.cmake b/cmake/filesystem/mime_type/mime_type_register.cmake similarity index 100% rename from cmake/file/mime_type_register.cmake rename to cmake/filesystem/mime_type/mime_type_register.cmake diff --git a/cmake/file/mime_type_register_default.cmake b/cmake/filesystem/mime_type/mime_type_register_default.cmake similarity index 100% rename from cmake/file/mime_type_register_default.cmake rename to cmake/filesystem/mime_type/mime_type_register_default.cmake diff --git a/cmake/filesystem/cd.cmake b/cmake/filesystem/navigation/cd.cmake similarity index 100% rename from cmake/filesystem/cd.cmake rename to cmake/filesystem/navigation/cd.cmake diff --git a/cmake/filesystem/dirs.cmake b/cmake/filesystem/navigation/dirs.cmake similarity index 100% rename from cmake/filesystem/dirs.cmake rename to cmake/filesystem/navigation/dirs.cmake diff --git a/cmake/filesystem/popd.cmake b/cmake/filesystem/navigation/popd.cmake similarity index 100% rename from cmake/filesystem/popd.cmake rename to cmake/filesystem/navigation/popd.cmake diff --git a/cmake/filesystem/pushd.cmake b/cmake/filesystem/navigation/pushd.cmake similarity index 100% rename from cmake/filesystem/pushd.cmake rename to cmake/filesystem/navigation/pushd.cmake diff --git a/cmake/filesystem/navigation/pushtmp.cmake b/cmake/filesystem/navigation/pushtmp.cmake new file mode 100644 index 00000000..ca08f955 --- /dev/null +++ b/cmake/filesystem/navigation/pushtmp.cmake @@ -0,0 +1,7 @@ +function(pushtmp) + mktemp() + ans(dir) + pushd("${dir}") + return_ans() + +endfunction() \ No newline at end of file diff --git a/cmake/filesystem/pwd.cmake b/cmake/filesystem/navigation/pwd.cmake similarity index 100% rename from cmake/filesystem/pwd.cmake rename to cmake/filesystem/navigation/pwd.cmake diff --git a/cmake/filesystem/cp.cmake b/cmake/filesystem/operations/cp.cmake similarity index 90% rename from cmake/filesystem/cp.cmake rename to cmake/filesystem/operations/cp.cmake index 1b770d9c..86087746 100644 --- a/cmake/filesystem/cp.cmake +++ b/cmake/filesystem/operations/cp.cmake @@ -1,41 +1,41 @@ -# copies the specified path to the specified target -# if last argument is a existing directory all previous files will be copied there -# else only two arguments are allow source and target -# cp( ) -# cp([ ...] ) -function(cp) - set(args ${ARGN}) - list_pop_back(args) - ans(target) - - list_length(args) - ans(len) - path("${target}") - ans(target) - # single move - - if(NOT IS_DIRECTORY "${target}" ) - if(NOT "${len}" EQUAL "1") - message(FATAL_ERROR "wrong usage for cp() exactly one source file needs to be specified") - endif() - path("${args}") - ans(source) - # this just has to be terribly slow... - # i am missing a direct - cmake(-E "copy" "${source}" "${target}" --return-code) - ans(ret) - if(NOT "${ret}" STREQUAL 0) - message("failed to copy ${source} to ${target}") - endif() - return() - endif() - - - paths(${args}) - ans(paths) - file(COPY ${paths} DESTINATION "${target}") - - - return() -endfunction() - +# copies the specified path to the specified target +# if last argument is a existing directory all previous files will be copied there +# else only two arguments are allow source and target +# cp( ) +# cp([ ...] ) +function(cp) + set(args ${ARGN}) + list_pop_back(args) + ans(target) + + list_length(args) + ans(len) + path("${target}") + ans(target) + # single move + + if(NOT IS_DIRECTORY "${target}" ) + if(NOT "${len}" EQUAL "1") + message(FATAL_ERROR "wrong usage for cp() exactly one source file needs to be specified") + endif() + path("${args}") + ans(source) + # this just has to be terribly slow... + # i am missing a direct + cmake(-E "copy" "${source}" "${target}" --exit-code) + ans(ret) + if(NOT "${ret}" STREQUAL 0) + message("failed to copy ${source} to ${target}") + endif() + return() + endif() + + + paths(${args}) + ans(paths) + file(COPY ${paths} DESTINATION "${target}") + + + return() +endfunction() + diff --git a/cmake/filesystem/cp_content.cmake b/cmake/filesystem/operations/cp_content.cmake similarity index 100% rename from cmake/filesystem/cp_content.cmake rename to cmake/filesystem/operations/cp_content.cmake diff --git a/cmake/filesystem/cp_dir.cmake b/cmake/filesystem/operations/cp_dir.cmake similarity index 66% rename from cmake/filesystem/cp_dir.cmake rename to cmake/filesystem/operations/cp_dir.cmake index 6bf7ce8e..64772e12 100644 --- a/cmake/filesystem/cp_dir.cmake +++ b/cmake/filesystem/operations/cp_dir.cmake @@ -2,10 +2,10 @@ function(cp_dir source_dir target_dir) path_qualify(source_dir) path_qualify(target_dir) - cmake(-E copy_directory "${source_dir}" "${target_dir}" --return-code) + cmake(-E copy_directory "${source_dir}" "${target_dir}" --exit-code) ans(error) if(error) - message(FATAL_ERROR "failed to copy contents of '${source_dir}' to '${target_dir}' ") + message(FATAL_ERROR "failed to copy contents of '${source_dir}' to '${target_dir}' this often happens when file names are too long ") endif() return_ref(target_dir) endfunction() diff --git a/cmake/filesystem/cp_glob.cmake b/cmake/filesystem/operations/cp_glob.cmake similarity index 100% rename from cmake/filesystem/cp_glob.cmake rename to cmake/filesystem/operations/cp_glob.cmake diff --git a/cmake/filesystem/mkdir.cmake b/cmake/filesystem/operations/mkdir.cmake similarity index 100% rename from cmake/filesystem/mkdir.cmake rename to cmake/filesystem/operations/mkdir.cmake diff --git a/cmake/filesystem/mkdirs.cmake b/cmake/filesystem/operations/mkdirs.cmake similarity index 100% rename from cmake/filesystem/mkdirs.cmake rename to cmake/filesystem/operations/mkdirs.cmake diff --git a/cmake/filesystem/mktemp.cmake b/cmake/filesystem/operations/mktemp.cmake similarity index 96% rename from cmake/filesystem/mktemp.cmake rename to cmake/filesystem/operations/mktemp.cmake index c6e45d41..4da5fc53 100644 --- a/cmake/filesystem/mktemp.cmake +++ b/cmake/filesystem/operations/mktemp.cmake @@ -1,9 +1,9 @@ -# creates a temporary directory -# you can specify an optional parent directory in which it should be created -# usage: mktemp([where])-> -function(mktemp) - path_temp(${ARGN}) - ans(path) - mkdir("${path}") - return_ref(path) -endfunction() +# creates a temporary directory +# you can specify an optional parent directory in which it should be created +# usage: mktemp([where])-> +function(mktemp) + path_temp(${ARGN}) + ans(path) + mkdir("${path}") + return_ref(path) +endfunction() diff --git a/cmake/filesystem/mv.cmake b/cmake/filesystem/operations/mv.cmake similarity index 93% rename from cmake/filesystem/mv.cmake rename to cmake/filesystem/operations/mv.cmake index 8d03dd00..f405cf1f 100644 --- a/cmake/filesystem/mv.cmake +++ b/cmake/filesystem/operations/mv.cmake @@ -1,33 +1,33 @@ -# moves the specified path to the specified target -# if last argument is a existing directory all previous files will be moved there -# else only two arguments are allow source and target -# mv( ) -# mv([ ...] ) -function(mv) - set(args ${ARGN}) - list_pop_back(args) - ans(target) - - list_length(args) - ans(len) - path("${target}") - ans(target) - # single move - if(NOT IS_DIRECTORY "${target}" ) - if(NOT "${len}" EQUAL "1") - message(FATAL_ERROR "wrong usage for mv() exactly one source file needs to be specified") - endif() - path("${args}") - ans(source) - file(RENAME "${source}" "${target}") - return() - endif() - - foreach(source ${args}) - file_name("${source}") - ans(fn) - mv("${source}" "${target}/${fn}") - endforeach() - - return() -endfunction() +# moves the specified path to the specified target +# if last argument is a existing directory all previous files will be moved there +# else only two arguments are allow source and target +# mv( ) +# mv([ ...] ) +function(mv) + set(args ${ARGN}) + list_pop_back(args) + ans(target) + + list_length(args) + ans(len) + path("${target}") + ans(target) + # single move + if(NOT IS_DIRECTORY "${target}" ) + if(NOT "${len}" EQUAL "1") + message(FATAL_ERROR "wrong usage for mv() exactly one source file needs to be specified") + endif() + path("${args}") + ans(source) + file(RENAME "${source}" "${target}") + return() + endif() + + foreach(source ${args}) + path_file_name("${source}") + ans(fn) + mv("${source}" "${target}/${fn}") + endforeach() + + return() +endfunction() diff --git a/cmake/filesystem/rm.cmake b/cmake/filesystem/operations/rm.cmake similarity index 95% rename from cmake/filesystem/rm.cmake rename to cmake/filesystem/operations/rm.cmake index 75124c9d..d314afa6 100644 --- a/cmake/filesystem/rm.cmake +++ b/cmake/filesystem/operations/rm.cmake @@ -1,20 +1,20 @@ -# removes the specified paths if -r is passed it will also remove subdirectories -# rm([-r] [ ...]) -# files names are qualified using pwd() see path() -function(rm) - set(args ${ARGN}) - list_extract_flag(args -r) - ans(recurse) - paths("${args}") - ans(paths) - set(cmd) - if(recurse) - set(cmd REMOVE_RECURSE) - else() - set(cmd REMOVE) - endif() - - file(${cmd} "${paths}") - return() -endfunction() - +# removes the specified paths if -r is passed it will also remove subdirectories +# rm([-r] [ ...]) +# files names are qualified using pwd() see path() +function(rm) + set(args ${ARGN}) + list_extract_flag(args -r) + ans(recurse) + paths("${args}") + ans(paths) + set(cmd) + if(recurse) + set(cmd REMOVE_RECURSE) + else() + set(cmd REMOVE) + endif() + + file(${cmd} "${paths}") + return() +endfunction() + diff --git a/cmake/filesystem/touch.cmake b/cmake/filesystem/operations/touch.cmake similarity index 52% rename from cmake/filesystem/touch.cmake rename to cmake/filesystem/operations/touch.cmake index a11c74ee..8cad39fb 100644 --- a/cmake/filesystem/touch.cmake +++ b/cmake/filesystem/operations/touch.cmake @@ -24,34 +24,6 @@ function(touch path) return_ref(path) endfunction() - #else() - # function(touch path) - # path("${path}") - # ans(path) -# -# # set(args ${ARGN}) -# # list_extract_flag(args --nocreate) -# # ans(nocreate) -# -# -# -# # set(cmd touch) -# # if(nocreate) -# # set(cmd touch_nocreate) -# -# # endif() -# -# # cmake(-E ${cmd} "${path}" --result) -# # ans(res) -# # json_print(${res}) -# # map_tryget(${res} result) -# # ans(erro) -# # if(erro) -# # message(FATAL_ERROR "faild") -# # endif() -# # return_ref(path) -# # endfunction() - #endif() touch("${path}") return_ans() endfunction() diff --git a/cmake/filesystem/parent_dir.cmake b/cmake/filesystem/parent_dir.cmake deleted file mode 100644 index 688a21bc..00000000 --- a/cmake/filesystem/parent_dir.cmake +++ /dev/null @@ -1,7 +0,0 @@ -# returns the directory of the specified file -function(parent_dir path) - path("${path}") - ans(path) - get_filename_component(res "${path}" PATH) - return_ref(res) -endfunction() \ No newline at end of file diff --git a/cmake/filesystem/parent_dir_name.cmake b/cmake/filesystem/parent_dir_name.cmake deleted file mode 100644 index bafc378e..00000000 --- a/cmake/filesystem/parent_dir_name.cmake +++ /dev/null @@ -1,7 +0,0 @@ - -function(parent_dir_name) - path("${ARGN}") - ans(path) - path_component("${path}" --file-name-ext) - return_ans() -endfunction() \ No newline at end of file diff --git a/cmake/filesystem/path.cmake b/cmake/filesystem/path/path.cmake similarity index 100% rename from cmake/filesystem/path.cmake rename to cmake/filesystem/path/path.cmake diff --git a/cmake/file/path_combine.cmake b/cmake/filesystem/path/path_combine.cmake similarity index 100% rename from cmake/file/path_combine.cmake rename to cmake/filesystem/path/path_combine.cmake diff --git a/cmake/filesystem/path_component.cmake b/cmake/filesystem/path/path_component.cmake similarity index 100% rename from cmake/filesystem/path_component.cmake rename to cmake/filesystem/path/path_component.cmake diff --git a/cmake/filesystem/path/path_extension.cmake b/cmake/filesystem/path/path_extension.cmake new file mode 100644 index 00000000..1696e308 --- /dev/null +++ b/cmake/filesystem/path/path_extension.cmake @@ -0,0 +1,10 @@ +## `()->` +## +## retuns the extension of the specified path +## +function(path_extension path) + path("${path}") + ans(path) + get_filename_component(res "${path}" EXT) + return_ref(res) +endfunction() \ No newline at end of file diff --git a/cmake/filesystem/file_name.cmake b/cmake/filesystem/path/path_file_name.cmake similarity index 90% rename from cmake/filesystem/file_name.cmake rename to cmake/filesystem/path/path_file_name.cmake index c20d3801..f387eb04 100644 --- a/cmake/filesystem/file_name.cmake +++ b/cmake/filesystem/path/path_file_name.cmake @@ -1,16 +1,16 @@ -# returns the name of the file without the directory -# if -we is specified the extensions is dropped -function(file_name path) - set(args ${ARGN}) - list_extract_flag(args -we) - ans(without_extension) - if(without_extension) - set(cmd NAME_WE) - else() - set(cmd NAME) - endif() - path("${path}") - ans(path) - get_filename_component(res "${path}" ${cmd}) - return_ref(res) +# returns the name of the file without the directory +# if -we is specified the extensions is dropped +function(path_file_name path) + set(args ${ARGN}) + list_extract_flag(args -we) + ans(without_extension) + if(without_extension) + set(cmd NAME_WE) + else() + set(cmd NAME) + endif() + path("${path}") + ans(path) + get_filename_component(res "${path}" ${cmd}) + return_ref(res) endfunction() \ No newline at end of file diff --git a/cmake/filesystem/path/path_issubdir.cmake b/cmake/filesystem/path/path_issubdir.cmake new file mode 100644 index 00000000..fe9f3baa --- /dev/null +++ b/cmake/filesystem/path/path_issubdir.cmake @@ -0,0 +1,10 @@ +## `(> )->` +## +## returns true iff subdir is or is below path +function(path_issubdir subdir path) + set(path ${ARGN}) + path_qualify(path) + path_qualify(subdir) + string_starts_with("${subdir}" "${path}") + return_ans() +endfunction() diff --git a/cmake/filesystem/path/path_parent_dir.cmake b/cmake/filesystem/path/path_parent_dir.cmake new file mode 100644 index 00000000..2eaa2920 --- /dev/null +++ b/cmake/filesystem/path/path_parent_dir.cmake @@ -0,0 +1,9 @@ +## `()->` +## +## returns the parent directory of the specified file or folder +## +function(path_parent_dir path) + path_qualify(path) + get_filename_component(res "${path}" PATH) + return_ref(res) +endfunction() \ No newline at end of file diff --git a/cmake/filesystem/path/path_parent_dir_name.cmake b/cmake/filesystem/path/path_parent_dir_name.cmake new file mode 100644 index 00000000..b36dc36c --- /dev/null +++ b/cmake/filesystem/path/path_parent_dir_name.cmake @@ -0,0 +1,11 @@ +## `()->` +## +## returns the name of the directory in which the specified file or folder resides +function(path_parent_dir_name) + path("${ARGN}") + ans(path) + path_parent_dir("${path}") + ans(path) + path_component("${path}" --file-name-ext) + return_ans() +endfunction() \ No newline at end of file diff --git a/cmake/file/path_parent_dirs.cmake b/cmake/filesystem/path/path_parent_dirs.cmake similarity index 100% rename from cmake/file/path_parent_dirs.cmake rename to cmake/filesystem/path/path_parent_dirs.cmake diff --git a/cmake/filesystem/path_qualify.cmake b/cmake/filesystem/path/path_qualify.cmake similarity index 100% rename from cmake/filesystem/path_qualify.cmake rename to cmake/filesystem/path/path_qualify.cmake diff --git a/cmake/filesystem/path_qualify_from.cmake b/cmake/filesystem/path/path_qualify_from.cmake similarity index 100% rename from cmake/filesystem/path_qualify_from.cmake rename to cmake/filesystem/path/path_qualify_from.cmake diff --git a/cmake/file/path_relative.cmake b/cmake/filesystem/path/path_relative.cmake similarity index 100% rename from cmake/file/path_relative.cmake rename to cmake/filesystem/path/path_relative.cmake diff --git a/cmake/file/path_split.cmake b/cmake/filesystem/path/path_split.cmake similarity index 100% rename from cmake/file/path_split.cmake rename to cmake/filesystem/path/path_split.cmake diff --git a/cmake/filesystem/path_temp.cmake b/cmake/filesystem/path/path_temp.cmake similarity index 92% rename from cmake/filesystem/path_temp.cmake rename to cmake/filesystem/path/path_temp.cmake index d68e8b9c..1d98e0e4 100644 --- a/cmake/filesystem/path_temp.cmake +++ b/cmake/filesystem/path/path_temp.cmake @@ -4,7 +4,7 @@ function(path_temp) set(args ${ARGN}) if("${args}_" STREQUAL "_") - oocmake_config(temp_dir) + cmakepp_config(temp_dir) ans(tmp_dir) set(args "${tmp_dir}") else() diff --git a/cmake/file/path_to_map.cmake b/cmake/filesystem/path/path_to_map.cmake similarity index 100% rename from cmake/file/path_to_map.cmake rename to cmake/filesystem/path/path_to_map.cmake diff --git a/cmake/filesystem/path_vary.cmake b/cmake/filesystem/path/path_vary.cmake similarity index 96% rename from cmake/filesystem/path_vary.cmake rename to cmake/filesystem/path/path_vary.cmake index c69169af..1d50ae5d 100644 --- a/cmake/filesystem/path_vary.cmake +++ b/cmake/filesystem/path/path_vary.cmake @@ -1,19 +1,19 @@ - -# varies the specified path until it does not exist -# this is done by appending a random string at the end of the path -# todo: allow something like map_format or a callback to vary a path -function(path_vary path) - path("${path}") - ans(base_path) - - set(path "${base_path}") - while(true) - - if(NOT EXISTS "${path}") - return("${path}") - endif() - - string(RANDOM rnd) - set(path "${base_path}${rnd}") - endwhile() + +# varies the specified path until it does not exist +# this is done by appending a random string at the end of the path +# todo: allow something like map_format or a callback to vary a path +function(path_vary path) + path("${path}") + ans(base_path) + + set(path "${base_path}") + while(true) + + if(NOT EXISTS "${path}") + return("${path}") + endif() + + string(RANDOM rnd) + set(path "${base_path}${rnd}") + endwhile() endfunction() \ No newline at end of file diff --git a/cmake/filesystem/paths.cmake b/cmake/filesystem/path/paths.cmake similarity index 95% rename from cmake/filesystem/paths.cmake rename to cmake/filesystem/path/paths.cmake index cb7d438d..4f28c750 100644 --- a/cmake/filesystem/paths.cmake +++ b/cmake/filesystem/path/paths.cmake @@ -1,11 +1,11 @@ - -# qualify multiple paths (argn) -function(paths) - set(res) - foreach(path ${ARGN}) - path("${path}") - ans(path) - list(APPEND res ${path}) - endforeach() - return_ref(res) + +# qualify multiple paths (argn) +function(paths) + set(res) + foreach(path ${ARGN}) + path("${path}") + ans(path) + list(APPEND res ${path}) + endforeach() + return_ref(res) endfunction() \ No newline at end of file diff --git a/cmake/file/paths_make_relative.cmake b/cmake/filesystem/path/paths_make_relative.cmake similarity index 100% rename from cmake/file/paths_make_relative.cmake rename to cmake/filesystem/path/paths_make_relative.cmake diff --git a/cmake/file/paths_relative.cmake b/cmake/filesystem/path/paths_relative.cmake similarity index 100% rename from cmake/file/paths_relative.cmake rename to cmake/filesystem/path/paths_relative.cmake diff --git a/cmake/file/paths_to_map.cmake b/cmake/filesystem/path/paths_to_map.cmake similarity index 100% rename from cmake/file/paths_to_map.cmake rename to cmake/filesystem/path/paths_to_map.cmake diff --git a/cmake/formats/json/README.md b/cmake/formats/json/README.md index c712cc47..19d18c3f 100644 --- a/cmake/formats/json/README.md +++ b/cmake/formats/json/README.md @@ -51,7 +51,6 @@ This is done by creating a hash from the input string and using it as a cache ke * [json_indented](#json_indented) * [json_print](#json_print) * [json_read](#json_read) -* [json_serialize](#json_serialize) * [json_string_to_cmake](#json_string_to_cmake) * [json_tokenize](#json_tokenize) * [json_write](#json_write) @@ -120,12 +119,6 @@ This is done by creating a hash from the input string and using it as a cache ke -## `json_serialize` - - - - - ## `json_string_to_cmake` diff --git a/cmake/formats/json/json2.cmake b/cmake/formats/json/json2.cmake index a65687b8..ff9bb0a1 100644 --- a/cmake/formats/json/json2.cmake +++ b/cmake/formats/json/json2.cmake @@ -8,10 +8,8 @@ function(json2 input) checksum_string("${input}") ans(ck) file_cache_return_hit("${ck}") - ref_get(json2_language_definition) ans(lang) - map_new() ans(ctx) map_set(${ctx} input "${input}") @@ -19,6 +17,7 @@ function(json2 input) obj_setprototype(${ctx} "${lang}") #lang2(output json2 input "${input}" def "json") + lang(output ${ctx}) ans(res) file_cache_update("${ck}" ${res}) diff --git a/cmake/formats/json/json2_definition.cmake b/cmake/formats/json/json2_definition.cmake index fc03e0d1..dffba061 100644 --- a/cmake/formats/json/json2_definition.cmake +++ b/cmake/formats/json/json2_definition.cmake @@ -7,7 +7,7 @@ map() key("name") val("parse") key("function") - val("parse_string\(/1\ /2\ /3\ /4\ /5\)") + val("parse_string\(/0\ /1\ /2\ /3\ /4\)") key("input") val("input_ref") val("def") @@ -21,7 +21,7 @@ map() key("name") val("create\ input\ ref") key("function") - val("ref_setnew\(/1\)") + val("ref_setnew\(/0\)") key("input") val("input") key("output") diff --git a/cmake/formats/json/json_serialize.cmake b/cmake/formats/json/json_serialize.cmake deleted file mode 100644 index 09f68b04..00000000 --- a/cmake/formats/json/json_serialize.cmake +++ /dev/null @@ -1,105 +0,0 @@ -function(json_serialize value) - - message(WARNING "deprecated function") - set(recursive_args) - # indent - if(ARGN) - set(list_to_array) - list(FIND args "LIST_TO_ARRAY" idx) - if(NOT ${idx} LESS 0) - set(list_to_array true) - set(recursive_args LIST_TO_ARRAY) - endif() - - set(args ${ARGN}) - list(FIND args "INDENTED" idx) - if(NOT ${idx} LESS 0) - json_serialize( "${value}") - ans(json) - json_tokenize(tokens "${json}") - json_format_tokens(indented "${tokens}") - return_ref(indented) - endif() - - - endif() - - - # if value is empty return an empty string - if(NOT value) - return() - endif() - # if value is a not ref return a simple string value - ref_isvalid("${value}") - ans(is_ref) - if(NOT is_ref) - json_escape( "${value}") - ans(value) - set(value "\"${value}\"" ) - return_ref(value) - endif() - - # get ref type - # here map, list and * will be differantited - # resulting object, array and string respectively - set(ref_type) - ref_gettype("${value}") - ans(ref_type) - if("${ref_type}" STREQUAL map) - set(res) - map_keys(${value} ) - ans(keys) - foreach(key ${keys}) - # message("value '${value}' key '${key}'") - map_get(${value} ${key}) - ans(val) - #message_indent_push() - json_serialize( "${val}" ${recursive_args} ) - ans(serialized_value) - # message_indent_pop() - if(serialized_value) - set(res "${res},\"${key}\":${serialized_value}") - endif() - endforeach() - string(LENGTH "${res}" len) - if(${len} GREATER 0) - string(SUBSTRING "${res}" 1 -1 res) - endif() - - set(res "{${res}}") - return_ref(res) - elseif("${ref_type}" STREQUAL list) - ref_get( ${value} ) - ans(lst) - set(res "") - foreach(val ${lst}) - json_serialize( "${val}" ${recursive_args}) - ans(serialized_value) - set(res "${res},${serialized_value}") - endforeach() - - string(LENGTH "${res}" len) - if(${len} GREATER 0) - string(SUBSTRING "${res}" 1 -1 res) - endif() - set(res "[${res}]") - return_ref(res) - else() - ref_get( ${value} ) - ans(ref_value) - if(list_to_array) - list_new() - ans(lst) - ref_set(${lst} "${ref_value}") - json_serialize( "${lst}" ${recursive_args}) - ans(serialized_value) - return_ref(serialized_value) - endif() - - json_escape( "${ref_value}") - ans(ref_value) - - return_ref(ref_value) - - endif() -endfunction() \ No newline at end of file diff --git a/cmake/formats/json/json_tokenize.cmake b/cmake/formats/json/json_tokenize.cmake index b632faaa..0f8a68d1 100644 --- a/cmake/formats/json/json_tokenize.cmake +++ b/cmake/formats/json/json_tokenize.cmake @@ -1,7 +1,9 @@ function(json_tokenize result json) + set(regex "(\\{|\\}|:|,|\\[|\\]|\"(\\\\.|[^\"])*\")") string(REGEX MATCHALL "${regex}" matches "${json}") + # replace brackets with angular brackets because # normal brackes are not handled properly by cmake string(REPLACE ";[;" ";<;" matches "${matches}") diff --git a/cmake/function/README.md b/cmake/function/README.md index bcef357e..26e40fea 100644 --- a/cmake/function/README.md +++ b/cmake/function/README.md @@ -24,11 +24,12 @@ Functions in cmake are not variables - they have a separate global only scope in ### Function List +* [arguments_encoded_list](#arguments_encoded_list) +* [arguments_string](#arguments_string) * [bind](#bind) * [call](#call) * [check_function](#check_function) -* [curry](#curry) -* [curry2](#curry2) +* [curry_compile_encoded_list](#curry_compile_encoded_list) * [function_capture](#function_capture) * [function_help](#function_help) * [function_import](#function_import) @@ -42,6 +43,8 @@ Functions in cmake are not variables - they have a separate global only scope in * [function_string_get](#function_string_get) * [function_string_import](#function_string_import) * [function_string_rename](#function_string_rename) +* [invocation_argument_encoded_list](#invocation_argument_encoded_list) +* [invocation_argument_string](#invocation_argument_string) * [is_function](#is_function) * [is_function_cmake](#is_function_cmake) * [is_function_file](#is_function_file) @@ -56,31 +59,45 @@ Functions in cmake are not variables - they have a separate global only scope in ### Function Descriptions -## `bind` +## `arguments_encoded_list` + usage arguments_string(${ARGC}) => acutal arguments string + -## `call` +## `arguments_string` + returns the argument string which was passed to the parent function + it takes into considerations quoted arguments + todo: start and endindex -## `check_function` +## `bind` + + +## `call` + + -## `curry` + +## `check_function` + +## `curry_compile_encoded_list` + + (["[" "]"] "(" (|)* ")" ["=>" () ])-> -## `curry2` @@ -165,6 +182,18 @@ Functions in cmake are not variables - they have a separate global only scope in +## `invocation_argument_encoded_list` + + + + + +## `invocation_argument_string` + + + + + ## `is_function` diff --git a/cmake/function/arguments_encoded_list.cmake b/cmake/function/arguments_encoded_list.cmake new file mode 100644 index 00000000..a3dddc9e --- /dev/null +++ b/cmake/function/arguments_encoded_list.cmake @@ -0,0 +1,15 @@ + ## usage arguments_string(${ARGC}) => acutal arguments string + ## + macro(arguments_encoded_list __arg_count) + set(__arg_res) + if(${__arg_count} GREATER 0) + math(EXPR __last_arg_index "${__arg_count} - 1") + foreach(i RANGE 0 ${__last_arg_index} ) + string_encode_list("${ARGV${i}}") + list(APPEND __arg_res "${__ans}") + endforeach() + set(__ans "${__arg_res}") + else() + set(__ans) + endif() + endmacro() \ No newline at end of file diff --git a/cmake/function/arguments_string.cmake b/cmake/function/arguments_string.cmake new file mode 100644 index 00000000..852de138 --- /dev/null +++ b/cmake/function/arguments_string.cmake @@ -0,0 +1,20 @@ +## returns the argument string which was passed to the parent function +## it takes into considerations quoted arguments +## todo: start and endindex + macro(arguments_string __arg_count) + set(__arg_res) + if(${__arg_count} GREATER 0) + math(EXPR __last_arg_index "${__arg_count} - 1") + foreach(i RANGE 0 ${__last_arg_index}) + set(__current "${ARGV${i}}") + if("${__current}_" MATCHES "(^_$)|(;)|(\\\")") + set(__current "\"${__current}\"") + endif() + set(__arg_res "${__arg_res} ${__current}") + endforeach() + string(SUBSTRING "${__arg_res}" "1" "-1" __ans) + else() + set(__ans) + endif() + endmacro() + \ No newline at end of file diff --git a/cmake/function/bind.cmake b/cmake/function/bind.cmake index 49f79158..1dfc656a 100644 --- a/cmake/function/bind.cmake +++ b/cmake/function/bind.cmake @@ -1,3 +1,6 @@ +# is the same as function_capture. +# deprecate one of the two +# # binds variables to the function # by caputring their current value and storing # them diff --git a/cmake/function/call.cmake b/cmake/function/call.cmake index d3cd0e2a..1174e3d7 100644 --- a/cmake/function/call.cmake +++ b/cmake/function/call.cmake @@ -13,11 +13,12 @@ return_reset() set(__function_call_args ${ARGN}) - list_pop_back( __function_call_args) + list_pop_back(__function_call_args) ans(__function_call_paren_close) if(NOT "_${__function_call_paren_open}${__function_call_paren_close}" STREQUAL "_()") - message(WARNING "expected opening and closing parentheses for function '${ARGN}'") + message("open ${__function_call_paren_open} close ${__function_call_paren_close}") + message(WARNING "expected opening and closing parentheses for function '${__function_call_func}' '${ARGN}' '${__function_call_args}'") endif() if(COMMAND "${__function_call_func}") @@ -26,6 +27,9 @@ return_ans() endif() + + + if(DEFINED "${__function_call_func}") call("${${__function_call_func}}"(${__function_call_args})) return_ans() @@ -50,12 +54,9 @@ endif() - - - lambda_isvalid("${__function_call_func}") - ans(is_lambda) - if(is_lambda) - lambda_import("${__function_call_func}" __function_call_import) + lambda2_tryimport("${__function_call_func}" __function_call_import) + ans(success) + if(success) __function_call_import(${__function_call_args}) return_ans() endif() diff --git a/cmake/function/callable/call2.cmake b/cmake/function/callable/call2.cmake new file mode 100644 index 00000000..356c9b06 --- /dev/null +++ b/cmake/function/callable/call2.cmake @@ -0,0 +1,12 @@ +function(call2 callable) + callable("${callable}") + ans(callable) + callable_call("${callable}") + return_ans() +endfunction() + +function(call2 callable) + callable_function("${callable}") + eval("${__ans}(${ARGN})") + set(__ans ${__ans} PARENT_SCOPE) +endfunction() \ No newline at end of file diff --git a/cmake/function/callable/callable.cmake b/cmake/function/callable/callable.cmake new file mode 100644 index 00000000..9b5033fd --- /dev/null +++ b/cmake/function/callable/callable.cmake @@ -0,0 +1,23 @@ +function(callable input) + string(MD5 input_key "${input}" ) + get_propertY(callable GLOBAL PROPERTY "__global_callables.${input_key}") + + if(NOT callable) + callable_new("${input}") + ans(callable) + + checksum_string("${callable}") + ans(callable_key) + + map_set_hidden(__global_callables "${input_key}" ${callable}) + map_set_hidden(__global_callables "${callable_key}" ${callable}) + + map_get_special(${callable} callable_function) + ans(function) + + map_set_hidden(__global_callable_functions "${input_key}" ${function}) + map_set_hidden(__global_callable_functions "${callable_key}" ${function}) + + endif() + set(__ans ${callable} PARENT_SCOPE) +endfunction() diff --git a/cmake/function/callable/callable_call.cmake b/cmake/function/callable/callable_call.cmake new file mode 100644 index 00000000..924747b7 --- /dev/null +++ b/cmake/function/callable/callable_call.cmake @@ -0,0 +1,6 @@ + +function(callable_call callable) + map_get_special("${callable}" callable_function) + eval("${__ans}(${ARGN})") + set(__ans "${__ans}" PARENT_SCOPE) +endfunction() diff --git a/cmake/function/callable/callable_function.cmake b/cmake/function/callable/callable_function.cmake new file mode 100644 index 00000000..55000826 --- /dev/null +++ b/cmake/function/callable/callable_function.cmake @@ -0,0 +1,13 @@ + + +## returns the cmake function for the specified callable +function(callable_function input) + string(MD5 input_key "${input}" ) + get_propertY(callable_func GLOBAL PROPERTY "__global_callable_functions.${input_key}") + if(NOT callable_func) + callable("${input}") + ans(callable) + get_propertY(callable_func GLOBAL PROPERTY "__global_callable_functions.${input_key}") + endif() + set(__ans ${callable_func} PARENT_SCOPE) +endfunction() \ No newline at end of file diff --git a/cmake/function/callable/callable_new.cmake b/cmake/function/callable/callable_new.cmake new file mode 100644 index 00000000..dd37f3ff --- /dev/null +++ b/cmake/function/callable/callable_new.cmake @@ -0,0 +1,10 @@ +function(callable_new input) + + map_new() + ans(callable) + function_import("${input}") + ans(callable_func) + map_set_special("${callable}" callable_function "${callable_func}") + map_set_special("${callable}" callable_input "${input}" ) + return_ref(callable) +endfunction() \ No newline at end of file diff --git a/cmake/function/callable/is_callable.cmake b/cmake/function/callable/is_callable.cmake new file mode 100644 index 00000000..a072ea5a --- /dev/null +++ b/cmake/function/callable/is_callable.cmake @@ -0,0 +1,9 @@ + + function(is_callable callable) + map_get_special("${callable}" callable_function) + ans(func) + if(COMMAND "${func}") + return(true) + endif() + return(false) + endfunction() diff --git a/cmake/function/curry.cmake b/cmake/function/curry.cmake deleted file mode 100644 index 3a048f2d..00000000 --- a/cmake/function/curry.cmake +++ /dev/null @@ -1,82 +0,0 @@ - # curry a function - # let funcA(a b c d) -> return("${a}${b}${c}${d}") - # and curry(funcA(/2 33 /1 44) as funcB) - # funcB(22 55) -> 55332244 - function(curry func) - set(args ${ARGN}) - list_extract_labelled_value(args as) - ans(curried_function_name) - - if(NOT curried_function_name) - function_new() - ans(curried_function_name) - endif() - - # remove parentheses - list_pop_front( args) - ans(paren_open) - list_pop_back( args) - ans(paren_close) - - set(arguments_string) - set(call_string) - - set(bound_args) - list(LENGTH args len) - - - string_encode_bracket("${args}") - ans(args) - - set(indices) - foreach(arg ${args}) - - if("${arg}" MATCHES "^/([0-9]+)$") - # reorder argument - set(arg "${CMAKE_MATCH_1}") - list(APPEND indices "${arg}") - set(arg_name "__arg_${arg}") - set(call_string "${call_string} \"\${__arg_${arg}}\"") - else() - # curry single argument - cmake_string_escape("${arg}") - ans(arg) - string_decode_bracket("${arg}") - ans(arg) - set(call_string "${call_string} \"${arg}\"") - endif() - - endforeach() - - list(LENGTH indices len) - if("${len}" GREATER 1) - list(SORT indices) - endif() - - foreach(arg ${indices}) - set(arguments_string "${arguments_string} __arg_${arg}") - endforeach() - # message("leftovers: ${leftovers}") - - # if func is not a command import it - if(NOT COMMAND "${func}") - function_new() - ans(original_func) - function_import("${func}" as ${original_func} REDEFINE) - else() - set(original_func "${func}") - endif() - - set(evaluate -"function(${curried_function_name} ${arguments_string})${bound_args} - ${original_func}(${call_string} \${ARGN}) - return_ans() -endfunction()") - - - - #message("curry: ${evaluate}") - set_ans("") - eval("${evaluate}") - return_ref(curried_function_name) - endfunction() \ No newline at end of file diff --git a/cmake/function/curry2.cmake b/cmake/function/curry2.cmake deleted file mode 100644 index d97b7568..00000000 --- a/cmake/function/curry2.cmake +++ /dev/null @@ -1,13 +0,0 @@ -# laternative to curry (just one string argument) - function(curry2 str) - string(REPLACE " " ";" str "${str}") - string(REPLACE ")" ";);" str "${str}") - string(REPLACE "(" ";(;" str "${str}") - #string(REPLACE "\"" "\\\"" str "${str}") - - #message("curry2... '${str}'") - #string(REPLACE "" "" str "${str}") - - curry(${str} ${ARGN}) - return_ans() - endfunction() \ No newline at end of file diff --git a/cmake/function/curry3.cmake b/cmake/function/curry3.cmake new file mode 100644 index 00000000..36bd420f --- /dev/null +++ b/cmake/function/curry3.cmake @@ -0,0 +1,142 @@ +## (["[" "]"] "(" (|)* ")" ["=>" () ])-> +## +## +function(curry_compile_encoded_list out_func) + #arguments_encoded_list(${ARGC}) + #ans(arguments) + set(arguments ${ARGN}) + string_codes() + set(regex_evaluates_to "=>") + if("${arguments}" MATCHES "(.*);?${regex_evaluates_to};(.*)") + set(definition "${CMAKE_MATCH_1}") + set(invocation "${CMAKE_MATCH_2}") + else() + set(invocation "${arguments}") + set(definition) + endif() + + list_peek_front(invocation) + ans(invocation_capture) + + if("${invocation_capture}" MATCHES "^${bracket_open_code}(.*)${bracket_close_code}$") + string_decode_list("${CMAKE_MATCH_1}") + ans(invocation_capture) + list_pop_front(invocation) + else() + set(invocation_capture) + endif() + + set(capture_code) + foreach(capture ${invocation_capture}) + # todo capture by value + set(capture_code "${capture_code}\n set(${capture} \"${${capture}}\")") + endforeach() + + regex_cmake() + + list_pop_front(invocation) + ans(callable) + + string_decode_list("${callable}") + ans(callable) + + function_import("${callable}") + ans(invocation_name) + + set(invocation "${invocation_name};${invocation}") + if("${invocation}" MATCHES "^(${regex_cmake_identifier});\\(;(.*);\\)") + set(invocation_name "${CMAKE_MATCH_1}") + set(invocation_args "${CMAKE_MATCH_2}") + elseif("${invocation}" MATCHES "^(${regex_cmake_identifier})") + set(invocation_name "${CMAKE_MATCH_1}") + set(invocation_args "/*") + endif() + + if("${definition}" MATCHES "^(${regex_cmake_identifier});\\(;?(.*);\\)") + set(definition_name "${CMAKE_MATCH_1}") + set(definition_args "${CMAKE_MATCH_2}") + elseif("${definition}" MATCHES "^(${regex_cmake_identifier});?$") + set(definition_name "${CMAKE_MATCH_1}") + set(definition_args ) + elseif("${definition}" MATCHES "^\;?\\(;?(.*);\\)") + set(definition_name) + set(definition_args "${CMAKE_MATCH_1}") + endif() + + if(NOT definition_name) + function_new() + ans(definition_name) + endif() + + + + map_new() + ans(assignments) + + + set(arg_counter 0) + set(arg_name_prefix __arg_) + set(input_args) + + foreach(argument ${definition_args}) + string_decode_list("${argument}") + ans(argument) + set(argument_name "__${argument}") + map_set(${assignments} ${argument} "${argument_name}") + set(input_args "${input_args} ${argument_name}") + endforeach() + + set(regex_arg_replacement "\\/(${regex_cmake_identifier}|\\*)") + set(regex_pos_replacement "\\/(0|([1-9][0-9]*))") + set(output_args) + foreach(argument ${invocation_args}) + string_decode_list("${argument}") + ans(argument) + if("${argument}" MATCHES "^${regex_pos_replacement}$") + set(argument_id "${CMAKE_MATCH_1}") + set(argument_out "\${ARGV${argument_id}}") + elseif("${argument}" MATCHES "^${regex_arg_replacement}$") + set(argument_id "${CMAKE_MATCH_1}") + if("${argument_id}" STREQUAL "*") + set(argument_out "\${ARGN}") + else() + map_tryget("${assignments}" "${argument_id}") + ans(argument_out) + set(argument_out "\${${argument_out}}") + endif() + else() + argument_escape("${argument}") + ans(argument_out) + endif() + set(output_args "${output_args} ${argument_out}") + endforeach() + if(NOT "${output_args}_" STREQUAL "_") + string(SUBSTRING "${output_args}" 1 -1 output_args) + endif() + if(NOT "${input_args}_" STREQUAL "_") + string(SUBSTRING "${input_args}" 1 -1 input_args) + endif() + + set(code "function(${definition_name} ${input_args})${capture_code}\n ${invocation_name}(${output_args})\n return_ans()\nendfunction()") + set(${out_func} "${definition_name}" PARENT_SCOPE) + return_ref(code) + +endfunction() + +## +function(curry_compile) + arguments_encoded_list(${ARGC}) + ans(arguments) + curry_compile_encoded_list(__outfunc "${arguments}") + return_ans() +endfunction() + +## +function(curry3) + arguments_encoded_list(${ARGC}) + ans(arguments) + curry_compile_encoded_list(__outfunc "${arguments}") + ans(code) + eval("${code}") + return_ref(__outfunc) +endfunction() \ No newline at end of file diff --git a/cmake/function/function_capture.cmake b/cmake/function/function_capture.cmake index 2eb98d13..dfac60dc 100644 --- a/cmake/function/function_capture.cmake +++ b/cmake/function/function_capture.cmake @@ -24,4 +24,6 @@ function(function_capture callable) endfunction() ") return_ref(func_name) -endfunction() \ No newline at end of file +endfunction() + + diff --git a/cmake/function/function_import.cmake b/cmake/function/function_import.cmake index d14688b9..bd684030 100644 --- a/cmake/function/function_import.cmake +++ b/cmake/function/function_import.cmake @@ -11,20 +11,28 @@ function(function_import callable) message(FATAL_ERROR "no callable specified") endif() - if(COMMAND "${function_name}" AND function_name AND function_name STREQUAL "${callable}") - message(DEBUG LEVEL 6 "function '${function_name}' should be imported as '${target_name}' ... returning without operation") - return() + if(COMMAND "${callable}") + if("${function_name}_" STREQUAL "_" OR "${callable}_" STREQUAL "${function_name}_") + return_ref(callable) + endif() endif() - function_string_get("${callable}") - ans(function_string) + + + if(NOT function_name) - function_new() - ans(function_name) - set(redefine true) + if(COMMAND "${callable}") + set(function_name "${callable}") + return_ref(function_name) + else() + function_new() + ans(function_name) + set(redefine true) + endif() endif() + if(COMMAND "${function_name}" AND NOT redefine) if(once) return() @@ -32,8 +40,20 @@ function(function_import callable) message(FATAL_ERROR "cannot import '${callable}' as '${function_name}' because it already exists") endif() + + lambda2_tryimport("${callable}" "${function_name}") + ans(res) + if(res) + return_ref(function_name) + endif() + + + function_string_get("${callable}") + ans(function_string) + function_string_rename("${function_string}" "${function_name}") ans(function_string) + function_string_import("${function_string}") return_ref(function_name) diff --git a/cmake/function/function_new.cmake b/cmake/function/function_new.cmake index da8eeaf0..31339a29 100644 --- a/cmake/function/function_new.cmake +++ b/cmake/function/function_new.cmake @@ -21,7 +21,8 @@ function(function_new ) return_ref(id) endif() #message("making_id because ${id} alreading existers") - make_guid(id) + make_guid() + ans(id) set(id "${name_base}_${id}") endwhile() diff --git a/cmake/function/function_string_get.cmake b/cmake/function/function_string_get.cmake index a1384f72..9bedf0e5 100644 --- a/cmake/function/function_string_get.cmake +++ b/cmake/function/function_string_get.cmake @@ -43,10 +43,16 @@ function(function_string_get func) is_function_cmake(is_cmake_func "${func}") if(is_cmake_func) + ## todo: just return nothing as func is already correctly defined... set(source "macro(${func})\n ${func}(\${ARGN})\nendmacro()") return_ref(source) return() endif() + + if(NOT (is_string OR is_file OR is_cmake_func) ) + message(FATAL_ERROR "the following is not a function: '${func}' ") + endif() + return() lambda_parse("${func}") ans(parsed_lambda) @@ -54,11 +60,7 @@ function(function_string_get func) if(parsed_lambda) return_ref(parsed_lambda) return() - endif() + #endif() - if(NOT (is_string OR is_file OR is_cmake_func) ) - message(FATAL_ERROR "the following is not a function: '${func}' ") - endif() - return() endfunction() \ No newline at end of file diff --git a/cmake/function/invocation_argument_encoded_list.cmake b/cmake/function/invocation_argument_encoded_list.cmake new file mode 100644 index 00000000..93d7e8a2 --- /dev/null +++ b/cmake/function/invocation_argument_encoded_list.cmake @@ -0,0 +1,5 @@ + + function(invocation_argument_encoded_list) + arguments_encoded_list(${ARGC}) + return_ans() + endfunction() \ No newline at end of file diff --git a/cmake/function/invocation_argument_string.cmake b/cmake/function/invocation_argument_string.cmake new file mode 100644 index 00000000..e7503c17 --- /dev/null +++ b/cmake/function/invocation_argument_string.cmake @@ -0,0 +1,6 @@ + + + function(invocation_argument_string) + arguments_string(${ARGC}) + return_ans() + endfunction() \ No newline at end of file diff --git a/cmake/function/lambda/is_lambda.cmake b/cmake/function/lambda/is_lambda.cmake new file mode 100644 index 00000000..c1e887da --- /dev/null +++ b/cmake/function/lambda/is_lambda.cmake @@ -0,0 +1,7 @@ + +function(is_lambda callable) + if("${callable}" MATCHES "^\\[[a-zA-Z0-9_ ]*]*\\]\\([[a-zA-Z0-9_ ]*\\)") + return(true) + endif() + return(false) +endfunction() \ No newline at end of file diff --git a/cmake/function/lambda/lambda.cmake b/cmake/function/lambda/lambda.cmake index 9f80dea1..cd4360a2 100644 --- a/cmake/function/lambda/lambda.cmake +++ b/cmake/function/lambda/lambda.cmake @@ -1,16 +1,9 @@ - - -#converts a lambda expression into a valid function string -function(lambda result expression) - string(FIND "${expression}" "function" isfunc) - - if("${isfunc}" GREATER "-1") - set(${result} "${expression}" PARENT_SCOPE) - return() - endif() - string_replace_first("(" "function(lambda_func result " "${expression}") - ans(expression) - string_replace_first(")" ")\n" "${expression}") - ans(expression) - set(${result} "${expression}\nendfunction()" PARENT_SCOPE) +## +## returns the cmake function that this lambda was compiled to +function(lambda2 source) + lambda2_instanciate("${source}") + ans(lambda) + map_tryget(${lambda} function_name) + return_ans() endfunction() + diff --git a/cmake/function/lambda/lambda_compile.cmake b/cmake/function/lambda/lambda_compile.cmake new file mode 100644 index 00000000..f7a0d7f8 --- /dev/null +++ b/cmake/function/lambda/lambda_compile.cmake @@ -0,0 +1,47 @@ +## +## compiles a lambda expression to valid cmake source and returns it +## {{a}} -> ${a} +## ["[""]"]["("")"] [(";")*] +## +## +function(lambda2_compile source) + string_encode_list("${source}") + ans(source) + string_codes() + regex_cmake() + + string_take_whitespace(source) + + set(capture_group_regex "${bracket_open_code}([^${bracket_close_code}]*)${bracket_close_code}") + if("${source}" MATCHES "^(${capture_group_regex})(.*)") + set(capture "${CMAKE_MATCH_2}") + set(source "${CMAKE_MATCH_3}") + string(REPLACE " " ";" capture "${capture}") + else() + set(capture) + endif() + + string_take_whitespace(source) + if("${source}" MATCHES "^\\(([^\\)]*)\\)(.*)") + set(signature "${CMAKE_MATCH_1}") + set(source "${CMAKE_MATCH_2}") + else() + + endif() + + + + string_take_whitespace(source) + + lambda2_compile_source("${source}") + ans(cmake_source) + + + + + + map_capture_new(signature capture source cmake_source) + + return_ans() + +endfunction() diff --git a/cmake/function/lambda/lambda_compile_source.cmake b/cmake/function/lambda/lambda_compile_source.cmake new file mode 100644 index 00000000..47e5275d --- /dev/null +++ b/cmake/function/lambda/lambda_compile_source.cmake @@ -0,0 +1,62 @@ + + function(lambda2_compile_source source) + string(ASCII 5 string_token) + + ## remove semicolons and brackets + string_encode_list("${source}") + ans(source) + + # extract all delimited strings + regex_delimited_string(' ') + ans(regex_delimited_string) + string(REGEX MATCHALL "${regex_delimited_string}" strings "${source}") + string(REGEX REPLACE "${regex_delimited_string}" "${string_token}" source "${source}") + + + ## re add semicolons and brackets + string_decode_list("${source}") + ans(source) + + ## replace ; with \n and commas with ; + set(code) + foreach(line ${source}) + string(REPLACE "," ";" line "${line}") + set(code "${code}${line}\n") + endforeach() + + + ## resubistitute all extracted strings + while(true) + list_pop_front(strings) + ans(current_string) + if(NOT current_string) + break() + endif() + string_decode_delimited("${current_string}" ' ') + ans(current_string) + + string_decode_list("${current_string}") + ans(current_string) + + cmake_string_escape("${current_string}") + ans(current_string) + + string_replace_first("${string_token}" "\"${current_string}\"" "${code}") + ans(code) + endwhile() + + regex_cmake() + + ## replace {{}} with ${__ans} + string(REPLACE "{{}}" "${string_token}" code "${code}" ) + string(REGEX REPLACE "${string_token}" "${string_token}{__ans}" code "${code}") + + ## replace {{}} with ${} + string(REGEX REPLACE "{{(${regex_cmake_identifier})}}" "${string_token}{\\1}" code "${code}") + string(REPLACE "${string_token}" "\$" code "${code}" ) + + ## end with returns_ans which forwards last return value + set(code "${code}return_ans()") + return_ref(code) + + endfunction() \ No newline at end of file diff --git a/cmake/function/lambda/lambda_import.cmake b/cmake/function/lambda/lambda_import.cmake index 1870a51d..03f9e44b 100644 --- a/cmake/function/lambda/lambda_import.cmake +++ b/cmake/function/lambda/lambda_import.cmake @@ -1,7 +1,8 @@ -function(lambda_import lambda_expression function_name) - lambda_parse("${lambda_expression}") - ans(lambda_func) - function_import("${lambda_func}" as ${function_name} REDEFINE) - return_ans() -endfunction() - +function(lambda2_tryimport callable) + if("${callable}" MATCHES "^\\[[a-zA-Z0-9_ ]*]*\\]\\([[a-zA-Z0-9_ ]*\\)") + lambda2_instanciate("${callable}" ${ARGN}) + ans(res) + return_ref(res) + endif() + return() +endfunction() diff --git a/cmake/function/lambda/lambda_instanciate.cmake b/cmake/function/lambda/lambda_instanciate.cmake new file mode 100644 index 00000000..b58e2c58 --- /dev/null +++ b/cmake/function/lambda/lambda_instanciate.cmake @@ -0,0 +1,31 @@ + + + function(lambda2_instanciate source) + + lambda2_compile("${source}") + ans(lambda) + + map_tryget(${lambda} capture) + ans(captures) + set(capture_code) + foreach(capture ${captures}) + set(capture_code "${capture_code}\n set(${capture} \"${${capture}}\")") + endforeach() + + + set(function_name ${ARGN}) + if(NOT function_name) + function_new() + ans(function_name) + endif() + map_set(${lambda} function_name ${function_name}) + + map_tryget(${lambda} cmake_source) + ans(cmake_source) + map_tryget(${lambda} signature) + ans(signature) + set(source "function(${function_name} ${signature})${capture_code}\n${cmake_source}\nendfunction()") + eval("${source}") + map_set(${lambda} cmake_function "${source}") + return_ref(lambda) + endfunction() diff --git a/cmake/function/lambda/lambda_isvalid.cmake b/cmake/function/lambda/lambda_isvalid.cmake deleted file mode 100644 index 79e33ffc..00000000 --- a/cmake/function/lambda/lambda_isvalid.cmake +++ /dev/null @@ -1,17 +0,0 @@ - # returns true if given code is a valid lambda expression - function(lambda_isvalid code) - string_split_at_first(lambda_signature partB "${code}" "->") - # message("signature ${lambda_signature}") - string(STRIP "${lambda_signature}" lambda_signature) - string_ends_with("${lambda_signature}" ")") - ans(ok) - if(NOT ok) - return(false) - endif() - string_starts_with("${lambda_signature}" "(") - ans(ok) - if(NOT ok) - return(false) - endif() - return(true) - endfunction() diff --git a/cmake/function/lambda/lambda_new.cmake b/cmake/function/lambda/lambda_new.cmake deleted file mode 100644 index 0a7a540d..00000000 --- a/cmake/function/lambda/lambda_new.cmake +++ /dev/null @@ -1,7 +0,0 @@ - -function(lambda_new lambda_expression) - function_new() - ans(func) - lambda_import("${lambda_expression}" "${func}") - return_ref(func) -endfunction() \ No newline at end of file diff --git a/cmake/function/lambda/lambda_parse.cmake b/cmake/function/lambda/lambda_parse.cmake deleted file mode 100644 index fee3f9e0..00000000 --- a/cmake/function/lambda/lambda_parse.cmake +++ /dev/null @@ -1,52 +0,0 @@ - -# creates a function from the lambda expressio in code. -# syntax for lambdas: (arg1 arg2 ... argn)->COMMAND;COMMAND;... -# if no return() is called lambda will return_ans() at the end -# which returns whatever was returned last -# instead of ${var} syntax use $var for variables -# use {expr} for evaluating expressions -function(lambda_parse code) - string(REPLACE "'" "\"" code "${code}") - string(REPLACE ");" ")\n" code "${code}") - - string_replace_first("(" "function(lambda_func " "${code}") - ans(code) - string_replace_first(")->" ")\nset(__ans)\n" "${code}") - ans(code) - string(REGEX MATCHALL "{[^}]*}" expressions "${code}") - - if(expressions) - foreach(expression ${expressions}) - string(RANDOM tmp_var_name) - set(tmp_var_name "__tmp_var_${tmp_var_name}") - - string_slice("${expression}" 1 -2) - ans(eval_this) - set(eval_string "expr(\"${eval_this}\")\nans(${tmp_var_name})\n") - string_regex_escape("${expression}") - ans(expression) - string_regex_escape("${eval_string}") - ans(eval_string) - string(REPLACE "\\" "\\\\" eval_string "${eval_string}") - set(repl "\\1\n${eval_string}\\2\\\\\$${tmp_var_name}") - # message("replace ${repl}") - # message("expression '${expression}'") - set(regex "(\n)([^\n]*)(${expression})") - string(REGEX MATCH "${regex}" match "${code}") - # message("match ${match}\n") - string(REGEX REPLACE "${regex}" "${repl}" code "${code}") - endforeach() - string(REPLACE "\\." "." code "${code}") - string(REPLACE "\\$" "$" code "${code}") - endif() - - string(REGEX MATCHALL "(\\$[a-zA-Z0-9-_\\.]+)" matches "${code}") - list(REMOVE_DUPLICATES matches) - foreach(match ${matches}) - string(REPLACE "$" "\${" repl "${match}") - set(repl "${repl}}") - string(REPLACE "${match}" "${repl}" code "${code}") - endforeach() - set(code "${code}\nreturn_ans()\nendfunction()") - return_ref(code) - endfunction() \ No newline at end of file diff --git a/cmake/function/rcall.cmake b/cmake/function/rcall.cmake index bf985e7e..75dc211d 100644 --- a/cmake/function/rcall.cmake +++ b/cmake/function/rcall.cmake @@ -1,8 +1,8 @@ # allows a single line call with result # ie rcall(some_result = obj.getSomeInfo(arg1 arg2)) -function(rcall __rcall_result_name equals ) +function(rcall __rcall_result_name equals __callable) set_ans("") - call(${ARGN}) + call("${__callable}" ${ARGN}) ans(res) set(${__rcall_result_name} ${res} PARENT_SCOPE) return_ref(res) diff --git a/cmake/handlers/command_line_handler.cmake b/cmake/handlers/command_line_handler.cmake index 984094a0..48399e12 100644 --- a/cmake/handlers/command_line_handler.cmake +++ b/cmake/handlers/command_line_handler.cmake @@ -85,7 +85,7 @@ ## or handler object method(add_handler) function(${add_handler}) - handler(${ARGN}) + request_handler(${ARGN}) ans(handler) if(NOT handler) return() diff --git a/cmake/handlers/handler_default.cmake b/cmake/handlers/handler_default.cmake index 376e5762..fa883981 100644 --- a/cmake/handlers/handler_default.cmake +++ b/cmake/handlers/handler_default.cmake @@ -7,7 +7,7 @@ return() endif() function_new() - ans(callable) + ans(call_function) function_import(" function(funcname request response) map_tryget(\${request} input) @@ -17,16 +17,16 @@ map_set(\${response} output \"\${res}\") return(true) endfunction() - " as ${callable} REDEFINE) + " as ${call_function} REDEFINE) data("{ - callable:$callable, + callable:$call_function, display_name:$func, labels:$func }") ans(handler) - handler("${handler}") + request_handler("${handler}") return_ans() endfunction() diff --git a/cmake/handlers/handler_execute.cmake b/cmake/handlers/handler_execute.cmake index 3c347e50..5c2b4a3a 100644 --- a/cmake/handlers/handler_execute.cmake +++ b/cmake/handlers/handler_execute.cmake @@ -1,7 +1,7 @@ ## executes a handler function(handler_execute handler request) - handler(${handler}) + request_handler(${handler}) ans(handler) data(${request}) ans(request) @@ -18,8 +18,8 @@ else() assign(!response.handler = handler) map_tryget(${handler} callable) - ans(callable) - call(${callable}("${request}" "${response}")) + ans(callback) + call("${callback}"("${request}" "${response}")) ans(result) endif() return_ref(response) diff --git a/cmake/handlers/handler.cmake b/cmake/handlers/request_handler.cmake similarity index 95% rename from cmake/handlers/handler.cmake rename to cmake/handlers/request_handler.cmake index 545f7077..f12e6197 100644 --- a/cmake/handlers/handler.cmake +++ b/cmake/handlers/request_handler.cmake @@ -1,6 +1,6 @@ ## creates a handler ## -function(handler handler) +function(request_handler handler) data("${handler}") ans(handler) map_isvalid(${handler}) diff --git a/cmake/map/README.md b/cmake/map/README.md index 39f04e75..5a7eb23a 100644 --- a/cmake/map/README.md +++ b/cmake/map/README.md @@ -31,7 +31,6 @@ Due to the "variable variable" system (ie names of variables are string which ca * [map_tryget](#map_tryget) * [dfs](#dfs) * [dfs_callback](#dfs_callback) -* [dfs_recurse](#dfs_recurse) * [list_match](#list_match) * [map_all_paths](#map_all_paths) * [map_at](#map_at) @@ -71,6 +70,7 @@ Due to the "variable variable" system (ie names of variables are string which ca * [map_push_back](#map_push_back) * [map_push_front](#map_push_front) * [map_rename](#map_rename) +* [map_set_if_missing](#map_set_if_missing) * [map_to_keyvaluelist](#map_to_keyvaluelist) * [map_to_valuelist](#map_to_valuelist) * [map_unpack](#map_unpack) @@ -234,12 +234,6 @@ Due to the "variable variable" system (ie names of variables are string which ca -## `dfs_recurse` - - - - - ## `list_match` @@ -497,6 +491,12 @@ Due to the "variable variable" system (ie names of variables are string which ca +## `map_set_if_missing` + + + + + ## `map_to_keyvaluelist` diff --git a/cmake/map/bfs.cmake b/cmake/map/bfs.cmake index 74ae6806..c8956df8 100644 --- a/cmake/map/bfs.cmake +++ b/cmake/map/bfs.cmake @@ -4,9 +4,9 @@ function(bfs expand) queue_new() ans(queue) - curry(queue_push("${queue}" /1)) + curry3(() => queue_push("${queue}" /0)) ans(push) - curry(queue_pop("${queue}")) + curry3(() => queue_pop("${queue}")) ans(pop) graphsearch(EXPAND "${expand}" PUSH "${push}" POP "${pop}" ${ARGN}) endfunction() \ No newline at end of file diff --git a/cmake/map/core/map_append_string.cmake b/cmake/map/core/map_append_string.cmake index f27841f0..e20295cc 100644 --- a/cmake/map/core/map_append_string.cmake +++ b/cmake/map/core/map_append_string.cmake @@ -7,5 +7,4 @@ function(map_append_string map key str) endif() get_property(property_val GLOBAL PROPERTY "${map}.${key}" ) set_property(GLOBAL PROPERTY "${map}.${key}" "${property_val}${str}") - endfunction() \ No newline at end of file diff --git a/cmake/map/core/map_get_special.cmake b/cmake/map/core/map_get_special.cmake index f5918fdb..e2246d14 100644 --- a/cmake/map/core/map_get_special.cmake +++ b/cmake/map/core/map_get_special.cmake @@ -3,6 +3,8 @@ map_tryget("${map}" "__${key}__") return_ans() endfunction() + + ## faster macro(map_get_special map key) get_property(__ans GLOBAL PROPERTY "${map}.__${key}__") endmacro() \ No newline at end of file diff --git a/cmake/map/core/map_isvalid.cmake b/cmake/map/core/map_isvalid.cmake index 0fcb65bf..041e1d84 100644 --- a/cmake/map/core/map_isvalid.cmake +++ b/cmake/map/core/map_isvalid.cmake @@ -1,5 +1,6 @@ # returns true if ref is a valid reference and its type is 'map' function(map_isvalid ref ) + ref_isvalid("${ref}") ans(isref) if(NOT isref) diff --git a/cmake/map/dfs.cmake b/cmake/map/dfs.cmake index 3b7d381b..015652b3 100644 --- a/cmake/map/dfs.cmake +++ b/cmake/map/dfs.cmake @@ -4,9 +4,9 @@ function(dfs expand) stack_new() ans(stack) - curry(stack_push("${stack}" /1)) + curry3(() => stack_push("${stack}" /0)) ans(push) - curry(stack_pop("${stack}" )) + curry3(() => stack_pop("${stack}" )) ans(pop) graphsearch(EXPAND "${expand}" PUSH "${push}" POP "${pop}" ${ARGN}) endfunction() diff --git a/cmake/map/dfs_callback.cmake b/cmake/map/dfs_callback.cmake index 380913e8..8f804450 100644 --- a/cmake/map/dfs_callback.cmake +++ b/cmake/map/dfs_callback.cmake @@ -120,7 +120,7 @@ function(dfs_callback callback) endfunction() function(dfs_callback callback) - curry("${callback}"(/1) as dfs_callback_emit) + curry3(dfs_callback_emit => "${callback}"(/0) as dfs_callback_emit) map_new() ans(visited) diff --git a/cmake/map/dfs_recurse.cmake b/cmake/map/dfs_recurse.cmake deleted file mode 100644 index fa51b7b9..00000000 --- a/cmake/map/dfs_recurse.cmake +++ /dev/null @@ -1,79 +0,0 @@ -# runs dfs recursively -# expects a config object: -# { -# expand: (node)->node[] # expand the specified node, -# enter: (node)->void # called before successors are evaluated -# leave: (node)->void # called after node's successors were evaluated -# } -# expand has the following available vars -# - ${path} contains path to the current node (including current node) -# - ${parent} contains the node from which current node was called -# enter has the following available vars -# - all expand vars -# - ${successors} contains all direct successors of current node -# - ${enter} (boolish) can be checked to see if currently entering a node (if enter and leave callbacks are the same function) -# leave has the following available vars -# - all expand vars -# - ${successors} same as enter -# - ${leave} (boolish) can be checked to see if node is currently being left (if enter and leave callbacks are the same function) -# - ${visited} contains all nodes which were visited in recursive calls below current node -# visited may contain duplicates depending on the graph - - function(dfs_recurse config) - obj("${config}") - ans(dfs_config) - - function(dfs_inner current) - set(path ${path} ${current}) - # get successors - map_tryget(${dfs_config} expand) - ans(expand) - - if(NOT expand) - message(FATAL_ERROR "expected a expand function") - endif() - - - rcall(successors = "${expand}"(${current})) - - map_tryget(${dfs_config} enter) - ans(enter) - if(enter) - set(leave) - call("${enter}"(${current})) - endif() - set(enter) - - - - set(parentparent ${parent}) - set(parent ${current}) - foreach(successor ${successors}) - dfs_inner(${successor}) - endforeach() - set(parent ${parentparent}) - - set(visited ${visited} ${successors} PARENT_SCOPE) - set(visited ${visited} ${successors}) - - map_tryget(${dfs_config} leave) - ans(leave) - - if(leave) - set(enter) - call("${leave}"(${current})) - endif() - set(leave) - endfunction() - - - - set(visited) - foreach(root ${ARGN}) - set(path) - dfs_inner(${root}) - endforeach() - - return() - - endfunction() \ No newline at end of file diff --git a/cmake/map/helpers/map_capture.cmake b/cmake/map/helpers/map_capture.cmake index 89e6cf3b..fe4374ce 100644 --- a/cmake/map/helpers/map_capture.cmake +++ b/cmake/map/helpers/map_capture.cmake @@ -5,7 +5,7 @@ function(map_capture map ) ans(__reassign) list_extract_flag(__map_capture_args --notnull) ans(__not_null) - foreach(__map_capture_arg ${ARGN}) + foreach(__map_capture_arg ${__map_capture_args}) if(__reassign AND "${__map_capture_arg}" MATCHES "(.+)[:=](.+)") set(__map_capture_arg_key ${CMAKE_MATCH_1}) diff --git a/cmake/map/helpers/map_matches.cmake b/cmake/map/helpers/map_matches.cmake index a61bb474..92619e83 100644 --- a/cmake/map/helpers/map_matches.cmake +++ b/cmake/map/helpers/map_matches.cmake @@ -3,7 +3,8 @@ function(map_matches attrs) obj("${attrs}") ans(attrs) - curry(map_match_properties(/1 ${attrs})) +# curry(map_match_properties(/1 ${attrs})) + curry3(map_match_properties(/0 ${attrs})) return_ans() endfunction() diff --git a/cmake/map/helpers/map_set_if_missing.cmake b/cmake/map/helpers/map_set_if_missing.cmake new file mode 100644 index 00000000..9e802066 --- /dev/null +++ b/cmake/map/helpers/map_set_if_missing.cmake @@ -0,0 +1,9 @@ + + function(map_set_if_missing map prop) + map_has("${map}" "${prop}") + if(__ans) + return(false) + endif() + map_set("${map}" "${prop}") + return(true) + endfunction() diff --git a/cmake/navigation/README.md b/cmake/navigation/README.md new file mode 100644 index 00000000..a72c7ab6 --- /dev/null +++ b/cmake/navigation/README.md @@ -0,0 +1,157 @@ +# Navigation Functions + + + +After creating `maps` and `objects` it was only prudent to create something that enables ease of use for the user. + +Only using `map_set` and `map_get` like functions is very frustrating especially if the depth of an object graph is more than one. consider: +```cmake +map_tryget(${obj} a) +ans(val_a) +if(val_a) + map_tryget(${val_a} b) + ans(val_b) + ... +endif() +``` + + +So I created the `assign` function which brings great comfort to navigating a object graph. It supports assignment to and from any navigation expression. navigation expressions support property navigation and range based list access. + +Here are some **Examples**: + + + +## Function List + + +* [assign](#assign) +* [get](#get) +* [map_navigate](#map_navigate) +* [map_navigate_set](#map_navigate_set) +* [map_navigate_set_if_missing](#map_navigate_set_if_missing) +* [nav](#nav) +* [navigation_expression_parse](#navigation_expression_parse) +* [ref_keys](#ref_keys) +* [ref_nav_create_path](#ref_nav_create_path) +* [ref_nav_get](#ref_nav_get) +* [ref_nav_set](#ref_nav_set) +* [ref_prop_get](#ref_prop_get) +* [ref_prop_set](#ref_prop_set) + +## Function Descriptions + +## `assign` + + `([!] |("="|"+=" )) -> ` + + the assign function allows the user to perform some nonetrivial + operations that other programming languages allow + + Examples + + + + + +## `get` + + universal get function which allows you to get + from an object or map. only allows property names + returns nothing if navigting the object tree fails + + + + +## `map_navigate` + + + + + +## `map_navigate_set` + + + + + +## `map_navigate_set_if_missing` + + + + + +## `nav` + + + + + +## `navigation_expression_parse` + + + + + +## `ref_keys` + + + + + +## `ref_nav_create_path` + + + + + +## `ref_nav_get` + + `(> ["&"])->` + navigates the specified value and returns the value the navigation expression + points to. If the value does not exist nothing is returned + + if the expression is prepended by an ampersand `&` the current lvalue is returned. + + **Examples** + let `${data}` be `"{a:{b:{c:3},d:[{e:4},{e:5}]}}"` + then + * `ref_nav_get(${data} a) => {"b":{"c":3},"d":[{"e":4},{"e":5}]}` + * `ref_nav_get(${data} a.b.c) => 3` + * `ref_nav_get(${data} a.b.c.d) => null` + * `ref_nav_get(${data} a.d[1].e) => 5` + * `ref_nav_get(${data} a.d[0].e) => 4` + * `ref_nav_get(${data} a.d) => [{"e":4},{"e":5}]` + * `ref_nav_get(${data} ) => {"a":{"b":{"c":3},"d":[{"e":4},{"e":5}]}}` + * `ref_nav_get(${data} &a.b.c) => {"ref":{"c":3},"property":"c","range":null,"value":3}` + + + + +## `ref_nav_set` + + `(> ["!"] )->` + + sets the specified navigation expression to the the value + taking into consideration the base_value. + + + + + + + +## `ref_prop_get` + + + + + +## `ref_prop_set` + + + + + + + diff --git a/cmake/navigation/README.md.in b/cmake/navigation/README.md.in new file mode 100644 index 00000000..c276c8ae --- /dev/null +++ b/cmake/navigation/README.md.in @@ -0,0 +1,34 @@ +# Navigation Functions +<% + assign(function_files = glob("**.cmake" --relative)) +%> + + +After creating `maps` and `objects` it was only prudent to create something that enables ease of use for the user. + +Only using `map_set` and `map_get` like functions is very frustrating especially if the depth of an object graph is more than one. consider: +```cmake +map_tryget(${obj} a) +ans(val_a) +if(val_a) + map_tryget(${val_a} b) + ans(val_b) + ... +endif() +``` + + +So I created the `assign` function which brings great comfort to navigating a object graph. It supports assignment to and from any navigation expression. navigation expressions support property navigation and range based list access. + +Here are some **Examples**: + + + +## Function List + +<%= markdown_template_function_list(${function_files}) %> + +## Function Descriptions + +<%= markdown_template_function_descriptions(${function_files}) %> + diff --git a/cmake/navigation/assign.cmake b/cmake/navigation/assign.cmake index 0a6cdc83..4ebc1686 100644 --- a/cmake/navigation/assign.cmake +++ b/cmake/navigation/assign.cmake @@ -1,4 +1,4 @@ -## assign([!] |("="|"+=" )) -> +## `([!] |("="|"+=" )) -> ` ## ## the assign function allows the user to perform some nonetrivial ## operations that other programming languages allow diff --git a/cmake/navigation/navigation_expression_parse.cmake b/cmake/navigation/navigation_expression_parse.cmake index 61b654da..b7f1b024 100644 --- a/cmake/navigation/navigation_expression_parse.cmake +++ b/cmake/navigation/navigation_expression_parse.cmake @@ -2,7 +2,7 @@ function(navigation_expression_parse) string(REPLACE "." ";" expression "${ARGN}") string(REPLACE "[" "<" expression "${expression}" ) string(REPLACE "]" ">" expression "${expression}" ) - string(REGEX REPLACE "([<>][0-9:]*[<>])" ";\\1" expression "${expression}") + string(REGEX REPLACE "([<>][0-9:-]*[<>])" ";\\1" expression "${expression}") string(REGEX REPLACE "^;" "" expression "${expression}") return_ref(expression) endfunction() \ No newline at end of file diff --git a/cmake/navigation/ref_nav_get.cmake b/cmake/navigation/ref_nav_get.cmake index a4db2fea..7f6c8d6e 100644 --- a/cmake/navigation/ref_nav_get.cmake +++ b/cmake/navigation/ref_nav_get.cmake @@ -1,39 +1,73 @@ +## `(> ["&"])->` +## navigates the specified value and returns the value the navigation expression +## points to. If the value does not exist nothing is returned +## +## if the expression is prepended by an ampersand `&` the current lvalue is returned. +## +## **Examples**<% +## set(data_input "{a:{b:{c:3},d:[{e:4},{e:5}]}}") +## script("${data_input}") +## ans(data) +## function(ref_nav_get_example ) +## set(expr ${ARGN}) +## ref_nav_get("${data}" ${expr}) +## ans(res) +## json("${res}") +## ans(res) +## return("`ref_nav_get(\\\${data} ${expr}) => ${res}`") +## endfunction() +## set(asdas 123) +## %> +## let `${data}` be `@json(${data_input})` +## then +## * @ref_nav_get_example(a) +## * @ref_nav_get_example(a.b.c) +## * @ref_nav_get_example(a.b.c.d) +## * @ref_nav_get_example(a.d[1].e) +## * @ref_nav_get_example(a.d[0].e) +## * @ref_nav_get_example(a.d) +## * @ref_nav_get_example() +## * @ref_nav_get_example(&a.b.c) +function(ref_nav_get current_value) + set(expression ${ARGN}) + if("${expression}" MATCHES "^&(.*)") + set(return_lvalue true ) + set(expression "${CMAKE_MATCH_1}") + else() + set(return_lvalue false) + endif() - function(ref_nav_get current_value) - set(expression ${ARGN}) - string_take(expression "&") - ans(return_lvalue) + navigation_expression_parse("${expression}") + ans(expression) - navigation_expression_parse("${expression}") - ans(expression) - - set(current_ref) - set(current_property) - set(current_ranges) - foreach(current_expression ${expression}) - if("${current_expression}" MATCHES "^[<>].*[<>]$") - list_range_try_get(current_value "${current_expression}") - ans(current_value) - list(APPEND current_ranges ${current_expression}) - else() - map_isvalid("${current_value}") - ans(is_ref) - - if(NOT is_ref) - break() - endif() - set(current_ref "${current_value}") - set(current_property "${current_expression}") - set(current_ranges) - - ref_prop_get("${current_value}" "${current_expression}") - ans(current_value) + set(current_ref) + set(current_property) + set(current_ranges) + foreach(current_expression ${expression}) + if("${current_expression}" MATCHES "^[<>].*[<>]$") + list_range_try_get(current_value "${current_expression}") + ans(current_value) + list(APPEND current_ranges ${current_expression}) + else() + ref_isvalid("${current_value}") + #map_isvalid("${current_value}") + ans(is_ref) + if(NOT is_ref) + set(current_value) + break() endif() - endforeach() - if(return_lvalue) - map_capture_new(ref:current_ref property:current_property range:current_ranges value:current_value --reassign) - return_ans() + set(current_ref "${current_value}") + set(current_property "${current_expression}") + set(current_ranges) + + ref_prop_get("${current_value}" "${current_expression}") + ans(current_value) endif() - return_ref(current_value) + endforeach() + if(return_lvalue) + map_capture_new(ref:current_ref property:current_property range:current_ranges value:current_value --reassign) + return_ans() + endif() + return_ref(current_value) - endfunction() +endfunction() diff --git a/cmake/navigation/ref_nav_set.cmake b/cmake/navigation/ref_nav_set.cmake index 7653ce5d..4e795ab0 100644 --- a/cmake/navigation/ref_nav_set.cmake +++ b/cmake/navigation/ref_nav_set.cmake @@ -1,93 +1,98 @@ +## `(> ["!"] )->` +## +## sets the specified navigation expression to the the value +## taking into consideration the base_value. +## +## +## +function(ref_nav_set base_value expression) + string_take(expression "!") + ans(create_path) + + navigation_expression_parse("${expression}") + ans(expression) + set(expression ${expression}) + + set(current_value "${base_value}") + set(current_ranges) + set(current_property) + set(current_ref) + # this loop navigates through existing values using ranges and properties as navigation expressions + # the 4 vars declared before this comment will be defined + while(true) + list(LENGTH expression continue) + if(NOT continue) + break() + endif() + list_pop_front(expression) + ans(current_expression) - - function(ref_nav_set base_value expression) - string_take(expression "!") - ans(create_path) - - navigation_expression_parse("${expression}") - ans(expression) - set(expression ${expression}) - - set(current_value "${base_value}") - set(current_ranges) - set(current_property) - set(current_ref) - # this loop navigates through existing values using ranges and properties as navigation expressions - # the 4 vars declared before this comment will be defined - while(true) - list(LENGTH expression continue) - if(NOT continue) - break() - endif() - - list_pop_front(expression) - ans(current_expression) - - set(is_property true) - if("${current_expression}" MATCHES "^[<>].*[<>]$") - set(is_property false) - endif() - # print_vars(current_expression is_property) - if(is_property) - - map_isvalid("${current_value}") - ans(is_ref) - if(is_ref) - set(current_ref "${current_value}") - set(current_property "${current_expression}") - set(current_ranges) - else() - list_push_front(expression "${current_expression}") - break() - endif() - - ref_prop_get("${current_value}" "${current_expression}") - ans(current_value) + set(is_property true) + if("${current_expression}" MATCHES "^[<>].*[<>]$") + set(is_property false) + endif() + # print_vars(current_expression is_property) + if(is_property) + + #map_isvalid("${current_value}") + ref_isvalid("${current_value}") + ans(is_ref) + if(is_ref) + set(current_ref "${current_value}") + set(current_property "${current_expression}") + set(current_ranges) else() - list_range_try_get(current_value "${current_expression}") - ans(current_value) - list(APPEND current_ranges "${current_expression}") - endif() - endwhile() - - - - set(value ${ARGN}) - - # if the expressions are left and create_path is not specified - # this will cause an error else the rest of the path is created - list(LENGTH expression expression_count) - if(expression_count GREATER 0) - if(NOT create_path) - message(FATAL_ERROR "could not find path ${expression}") + list_push_front(expression "${current_expression}") + break() endif() - ref_nav_create_path("${expression}" ${value}) - ans(value) - endif() - ## get the last existing value - if(current_ref) - ref_prop_get("${current_ref}" "${current_property}") + ref_prop_get("${current_value}" "${current_expression}") ans(current_value) else() - set(current_value ${base_value}) + list_range_try_get(current_value "${current_expression}") + ans(current_value) + list(APPEND current_ranges "${current_expression}") endif() + endwhile() - ## if there are ranges set the interpret the value as a lsit and set the correct element - list(LENGTH current_ranges range_count) - if(range_count GREATER 0) - list_range_partial_write(current_value "${current_ranges}" "${value}") - else() - set(current_value "${value}") - endif() - ## either return a new base balue or set the property of the last existing ref - if(NOT current_ref) - set(base_value "${current_value}") - else() - ref_prop_set("${current_ref}" "${current_property}" "${current_value}") - endif() - return_ref(base_value) - endfunction() + set(value ${ARGN}) + + # if the expressions are left and create_path is not specified + # this will cause an error else the rest of the path is created + list(LENGTH expression expression_count) + if(expression_count GREATER 0) + if(NOT create_path) + message(FATAL_ERROR "could not find path ${expression}") + endif() + ref_nav_create_path("${expression}" ${value}) + ans(value) + endif() + + ## get the last existing value + if(current_ref) + ref_prop_get("${current_ref}" "${current_property}") + ans(current_value) + else() + set(current_value ${base_value}) + endif() + + ## if there are ranges set the interpret the value as a lsit and set the correct element + list(LENGTH current_ranges range_count) + if(range_count GREATER 0) + list_range_partial_write(current_value "${current_ranges}" "${value}") + else() + set(current_value "${value}") + endif() + + ## either return a new base balue or set the property of the last existing ref + if(NOT current_ref) + set(base_value "${current_value}") + else() + ref_prop_set("${current_ref}" "${current_property}" "${current_value}") + endif() + + return_ref(base_value) +endfunction() diff --git a/cmake/navigation/ref_prop_get.cmake b/cmake/navigation/ref_prop_get.cmake index 1d56c467..6f35fb56 100644 --- a/cmake/navigation/ref_prop_get.cmake +++ b/cmake/navigation/ref_prop_get.cmake @@ -8,4 +8,15 @@ map_tryget("${ref}" "${prop}") endif() return_ans() - endfunction() \ No newline at end of file + endfunction() + + ## faster + macro(ref_prop_get ref prop) + map_get_special("${ref}" object) + ans(isobject) + if(isobject) + obj_get("${ref}" "${prop}") + else() + map_tryget("${ref}" "${prop}") + endif() + endmacro() \ No newline at end of file diff --git a/cmake/object/README.md b/cmake/object/README.md index fbb3e88e..58b7f3ca 100644 --- a/cmake/object/README.md +++ b/cmake/object/README.md @@ -30,13 +30,16 @@ As is possible in JavaScript an Python I let you override default object operati * `__get_keys__ : ():` override the operation which returns the list of available keys. It is expected that all keys returned will are valid properties (they exist). * `__has__ : ( ):` overrides the has operation. MUST return true iff the object has a property called `` * `__member_call__ : ( >):>` this operation is invoked when `obj_member_call(...)` is called (and thus also when `call, rcall, etc` is called) overriding this function allows you to dispatch a call operation to the object member identified by `` with the specified `args` it should return the result of the specified operation. The `this` variable is always set to the object instance current instance. - * `__cast__` helper functions + * `__cast__` +* helper functions * `obj_declare_getter()` * `obj_declare_setter()` * `obj_declare_call()` * `obj_declare_member_call()` * `obj_declare_get_keys()` - * `obj_declare_has_key()` * `obj_declare_cast()` + * `obj_declare_has_key()` + * `obj_declare_cast()` + ``` new([Constructor]) returns a ref to a object diff --git a/cmake/package/cmakepp_project_on_package_install.cmake b/cmake/package/cmakepp_project_on_package_install.cmake index 1f509b6b..a787cb14 100644 --- a/cmake/package/cmakepp_project_on_package_install.cmake +++ b/cmake/package/cmakepp_project_on_package_install.cmake @@ -1,11 +1,17 @@ - ## - ## - ## hooks: - ## package_descriptor.cmakepp.hooks.on_install( ) - ## this hook is invoked if it exists. it is invoked before the on_load hook - ## this means that the project's exports were not loaded when the hook is called - ## however since cmake files are callable you can specify a local path - function(cmakepp_project_on_package_install project_handle package_handle) - package_handle_invoke_hook("${package_handle}" cmakepp.hooks.on_install ${project_handle} ${package_handle}) - endfunction() \ No newline at end of file +## +## +## hooks: +## package_descriptor.cmakepp.hooks.on_install( ) +## this hook is invoked if it exists. it is invoked before the on_load hook +## this means that the project's exports were not loaded when the hook is called +## however since cmake files are callable you can specify a local path +function(cmakepp_project_on_package_install project_handle package_handle) + package_handle_invoke_hook("${package_handle}" cmakepp.hooks.on_install ${project_handle} ${package_handle}) +endfunction() + +## register listener for the project_on_package_install event +## which directly after a package is installed in a project +## this code is executed as soon as cmakepp has finisehd loading +task_enqueue("[]()event_addhandler(project_on_package_install cmakepp_project_on_package_install)") + diff --git a/cmake/package/cmakepp_project_on_package_load.cmake b/cmake/package/cmakepp_project_on_package_load.cmake index 411c5c8d..a22d9d23 100644 --- a/cmake/package/cmakepp_project_on_package_load.cmake +++ b/cmake/package/cmakepp_project_on_package_load.cmake @@ -1,45 +1,51 @@ - ## - ## - ## imports all files specified in the package_handle's - ## package_descriptor.cmakepp.export property relative - ## to the package_handle.content_dir. the files are - ## included in which they were globbed. - ## - ## hooks: - ## package_descriptor.cmakepp.hooks.on_load( ): - ## after files were imported the hook stored under - ## package_descriptor.cmakepp.hooks.on_load is called - ## the value of on_load may be anything callable (file,function lambda) - ## the functions which were exported in the previous step - ## can be such callables. - ## - ## - ## events: - ## on_package_load( ): - ## emitted after cmake exports were loaded and packge's - ## on_load hook was invoked. - ## - function(cmakepp_project_on_package_load project_handle package_handle) - ## load the exports and include them once - assign(content_dir = package_handle.content_dir) - assign(export = package_handle.package_descriptor.cmakepp.export) - - if(IS_DIRECTORY "${content_dir}") - pushd("${content_dir}") - glob_ignore("${export}") - ans(paths) - popd() - - foreach(path ${paths}) - include_once("${path}") - endforeach() - endif() - - - ## call on_load hook - package_handle_invoke_hook(${package_handle} cmakepp.hooks.on_load ${project_handle} ${package_handle}) - - ## emit the onload event - event_emit(on_package_load ${project} ${package_handle}) - endfunction() \ No newline at end of file +## +## +## imports all files specified in the package_handle's +## package_descriptor.cmakepp.export property relative +## to the package_handle.content_dir. the files are +## included in which they were globbed. +## +## hooks: +## package_descriptor.cmakepp.hooks.on_load( ): +## after files were imported the hook stored under +## package_descriptor.cmakepp.hooks.on_load is called +## the value of on_load may be anything callable (file,function lambda) +## the functions which were exported in the previous step +## can be such callables. +## +## +## events: +## on_package_load( ): +## emitted after cmake exports were loaded and packge's +## on_load hook was invoked. +## +function(cmakepp_project_on_package_load project_handle package_handle) + ## load the exports and include them once + assign(content_dir = package_handle.content_dir) + assign(export = package_handle.package_descriptor.cmakepp.export) + + if(IS_DIRECTORY "${content_dir}") + pushd("${content_dir}") + glob_ignore("${export}") + ans(paths) + popd() + + foreach(path ${paths}) + include_once("${path}") + endforeach() + endif() + + + ## call on_load hook + package_handle_invoke_hook(${package_handle} cmakepp.hooks.on_load ${project_handle} ${package_handle}) + + ## emit the onload event + event_emit(on_package_load ${project} ${package_handle}) +endfunction() + +## register listener for the project_on_package_load event +## as soon as cmakepp loads +task_enqueue("[]()event_addhandler(project_on_package_load cmakepp_project_on_package_load)") + + diff --git a/cmake/package/cmakepp_project_on_package_save.cmake b/cmake/package/cmakepp_project_on_package_save.cmake new file mode 100644 index 00000000..9b5b9749 --- /dev/null +++ b/cmake/package/cmakepp_project_on_package_save.cmake @@ -0,0 +1,22 @@ + +## +## +## +## hooks: +## package_descriptor.cmakepp.hooks.on_save( ): +## +## +## events: +## on_package_save( ): +## +function(cmakepp_project_on_package_save project_handle package_handle) + ## call on_save hook + package_handle_invoke_hook(${package_handle} cmakepp.hooks.on_save ${project_handle} ${package_handle}) + + ## emit the on_package_save event + event_emit(on_package_save ${project} ${package_handle}) +endfunction() + +## register listener for the project_on_package_load event +## as soon as cmakepp loads +task_enqueue("[]()event_addhandler(project_on_package_save cmakepp_project_on_package_save)") diff --git a/cmake/package/cmakepp_project_on_package_uninstall.cmake b/cmake/package/cmakepp_project_on_package_uninstall.cmake new file mode 100644 index 00000000..604e53c4 --- /dev/null +++ b/cmake/package/cmakepp_project_on_package_uninstall.cmake @@ -0,0 +1,15 @@ + +## +## +## hooks: +## package_descriptor.cmakepp.hooks.on_install( ) +## this hook is invoked if it exists. it is invoked before the on_load hook +## this means that the project's exports were not loaded when the hook is called +## however since cmake files are callable you can specify a local path +function(cmakepp_project_on_package_uninstall project_handle package_handle) + package_handle_invoke_hook("${package_handle}" cmakepp.hooks.on_uninstall ${project_handle} ${package_handle}) +endfunction() + + +## register listener for the project_on_package_uninstall event +task_enqueue("[]()event_addhandler(project_on_package_uninstall cmakepp_project_on_package_uninstall)") diff --git a/cmake/package/package_handle/package_handle_filter.cmake b/cmake/package/package_handle/package_handle_filter.cmake new file mode 100644 index 00000000..27101ea0 --- /dev/null +++ b/cmake/package/package_handle/package_handle_filter.cmake @@ -0,0 +1,37 @@ +function(package_handle_filter __handles uri) + uri_coerce(uri) + + map_tryget(${uri} uri) + ans(uri_string) + + ## return all handles if query uri is ?* + if("${uri_string}" STREQUAL "?*") + return_ref(${__handles}) + endif() + + foreach(package_handle ${${__handles}}) + map_tryget(${package_handle} uri) + ans(package_uri) + if("${package_uri}" STREQUAL "${uri_string}") + return(${package_handle}) + endif() + endforeach() + + assign(id_query = uri.params.id) + if(id_query) + set(result) + foreach(package_handle ${${__handles}}) + assign(pid = package_handle.package_descriptor.id) + if("${pid}_" STREQUAL "${id_query}_") + list(APPEND result ${package_handle}) + endif() + endforeach() + return_ref(result) + endif() + + ## todo... + + return() + + + endfunction() \ No newline at end of file diff --git a/cmake/package/package_source/archive/package_source_push_archive.cmake b/cmake/package/package_source/archive/package_source_push_archive.cmake index 15d92c17..0f0dc252 100644 --- a/cmake/package/package_source/archive/package_source_push_archive.cmake +++ b/cmake/package/package_source/archive/package_source_push_archive.cmake @@ -1,54 +1,75 @@ - - - function(package_source_push_archive package_handle uri) - set(args ${ARGN}) - - ## parse and extract package_descriptor and package dir - package_handle("${package_handle}") - ans(package_handle) - - if(NOT package_handle) - return() + function(package_source_push_archive) + if("${ARGN}" MATCHES "(.*);=>;?(.*)") + set(source_args "${CMAKE_MATCH_1}") + set(args "${CMAKE_MATCH_2}") + else() + set(source_args ${ARGN}) + set(args) endif() + list_pop_front(source_args) + ans(source) - map_tryget("${package_handle}" package_descriptor) - ans(package_descriptor) + list_extract_flag(args --force) + ans(force) - map_tryget("${package_handle}" content_dir) - ans(source_dir) + list_pop_front(args) + ans(target_file) + ## used to pass format along + list_extract_labelled_keyvalue(args --format) + ans(format) - uri("${uri}") - ans(uri) - ## get fully qualified local path - uri_to_localpath("${uri}") - ans(archive_path) - path_qualify(archive_path) + path_qualify(target_file) - ## copy package content to a temporary directory - file_tempdir() + path_temp() ans(temp_dir) - package_source_push_path("${source_dir};${package_descriptor}" "${temp_dir}" --force) - - - ## pass format along - list_extract_labelled_keyvalue(args --format) - ans(format) + assign(package_handle = source.pull(${source_args} "${temp_dir}")) + assign(content_dir = package_handle.content_dir)# get content dir because pull may return somtehing different in case --reference is specified + + + ## possibly generate a filename if ${target_file} is a directory + if(IS_DIRECTORY "${target_file}") + set(mimetype ${format}) + list_extract_labelled_value(mimetype --format) + ans(mimetype) + if(NOT mimetype) + set(mimetype "application/x-gzip") + endif() + mime_type_get_extension("${mimetype}") + ans(extension) + format("{package_handle.package_descriptor.id}-{package_handle.package_descriptor.version}.{extension}") + ans(filename) + set(target_file "${target_file}/${filename}") + endif() + if(EXISTS "${target_file}") + if(NOT force) + error("cannot push: ${target_file} already exists") + return() + endif() + if(IS_DIRECTORY "${target_file}") + error("cannot push forced: ${target_file} is a directory") + return() + endif() + rm("${target_file}") + endif() ## compress all files in temp_dir into package - pushd("${temp_dir}") - compress("${archive_path}" "**" ${format}) + pushd("${content_dir}") + compress("${target_file}" "**" ${format}) popd() - ## delete temp dir + ## cleanup rm("${temp_dir}") - ## return valid package uri - uri_format("${archive_path}") - ans(result) + package_source_query_archive("${target_file}") + ans(package_uri) + + + ## set altered uri (now contains hash) + map_set(${package_handle} uri "${package_uri}") - return_ref(result) + return_ref(package_handle) endfunction() \ No newline at end of file diff --git a/cmake/package/package_source/archive/package_source_resolve_archive.cmake b/cmake/package/package_source/archive/package_source_resolve_archive.cmake index 92806e97..c1f9d198 100644 --- a/cmake/package/package_source/archive/package_source_resolve_archive.cmake +++ b/cmake/package/package_source/archive/package_source_resolve_archive.cmake @@ -2,6 +2,7 @@ ## ## resolves the specified uri to a unqiue immutable package handle function(package_source_resolve_archive uri) + uri("${uri}") ans(uri) @@ -19,9 +20,10 @@ function(package_source_resolve_archive uri) assign(archive_path = package_handle.archive_descriptor.path) ## search for the first package.cmake file in the archive - archive_match_files("${archive_path}" "(^|/)package\\.cmake" --single) + archive_match_files("${archive_path}" "([^;]+/)?package\\.cmake" --single) ans(package_descriptor_path) + if(package_descriptor_path) archive_read_file("${archive_path}" "${package_descriptor_path}") ans(package_descriptor_content) diff --git a/cmake/package/package_source/bitbucket/package_source_query_bitbucket.cmake b/cmake/package/package_source/bitbucket/package_source_query_bitbucket.cmake index 9367ba5b..24bde85c 100644 --- a/cmake/package/package_source/bitbucket/package_source_query_bitbucket.cmake +++ b/cmake/package/package_source/bitbucket/package_source_query_bitbucket.cmake @@ -68,8 +68,8 @@ function(package_source_query_bitbucket uri) if(ref) map_tryget("${ref}" commit) ans(hash) - set(uri "bitbucket:${user}/${repo}/branches/${default_branch}?hash=${hash}") - map_set(${package_handles} "bitbucket:${user}/${repo}/branches/${default_branch}?hash=${hash}" ${ref}) + set(package_uri "bitbucket:${user}/${repo}/branches/${default_branch}?hash=${hash}") + map_set(${package_handles} "${package_uri}" ${ref}) endif() endif() else() diff --git a/cmake/package/package_source/composite/package_source_pull_composite.cmake b/cmake/package/package_source/composite/package_source_pull_composite.cmake index 389a9bea..be7ef03c 100644 --- a/cmake/package/package_source/composite/package_source_pull_composite.cmake +++ b/cmake/package/package_source/composite/package_source_pull_composite.cmake @@ -26,8 +26,7 @@ ## use the package package source to pull the correct package ## and return the result - call(package_source.pull("${package_uri}" ${args})) - ans(package_handle) + assign(package_handle = package_source.pull("${package_uri}" ${args})) return_ref(package_handle) endfunction() diff --git a/cmake/package/package_source/composite/package_source_query_composite.cmake b/cmake/package/package_source/composite/package_source_query_composite.cmake index 8d9fdce7..80ef74c2 100644 --- a/cmake/package/package_source/composite/package_source_query_composite.cmake +++ b/cmake/package/package_source/composite/package_source_query_composite.cmake @@ -52,7 +52,7 @@ ## query the source ## args (especially --package-handle will be passed along) - rcall(current_result = source.query("${uri}" ${args})) + assign(current_result = source.query("${uri}" ${args})) if(return_package_handle) map_set(${current_result} package_source ${source}) diff --git a/cmake/package/package_source/composite/package_source_resolve_composite.cmake b/cmake/package/package_source/composite/package_source_resolve_composite.cmake index f8a6581b..b4415b82 100644 --- a/cmake/package/package_source/composite/package_source_resolve_composite.cmake +++ b/cmake/package/package_source/composite/package_source_resolve_composite.cmake @@ -30,7 +30,7 @@ map_tryget(${package_handle} uri) ans(uri) - rcall(package_handle = package_source.resolve("${uri}")) + assign(package_handle = package_source.resolve("${uri}")) if(package_handle) ## copy over package source to new package handle diff --git a/cmake/package/package_source/github/package_source_query_github.cmake b/cmake/package/package_source/github/package_source_query_github.cmake index 27d99fff..46d26f3f 100644 --- a/cmake/package/package_source/github/package_source_query_github.cmake +++ b/cmake/package/package_source/github/package_source_query_github.cmake @@ -66,8 +66,12 @@ function(package_source_query_github uri) ## only user results in non unique ids which have to be quried again github_repository_list("${user}") ans(repositories) - list_select(repositories "(repo)-> return('github:{repo.full_name}')") - ans(package_handles) + set(package_handles) + foreach(repo ${repositories}) + map_tryget(${repo} full_name) + ans(repo_name) + list(APPEND package_handles "github:${repo_name}") + endforeach() else() ## no user (not queried) too many results endif() diff --git a/cmake/package/package_source/github/package_source_resolve_github.cmake b/cmake/package/package_source/github/package_source_resolve_github.cmake index a92e3bc6..bb104b79 100644 --- a/cmake/package/package_source/github/package_source_resolve_github.cmake +++ b/cmake/package/package_source/github/package_source_resolve_github.cmake @@ -1,9 +1,4 @@ - function(github_get_file user repo ref path) - set(raw_uri "https://raw.githubusercontent.com/") - set(path_uri "${raw_uri}/${user}/${repo}/${ref}/${path}" ) - http_get("${path_uri}" ${ARGN}) - return_ans() - endfunction() + ## package_source_resolve_github() -> {} ## diff --git a/cmake/package/package_source/hg/package_source_resolve_hg.cmake b/cmake/package/package_source/hg/package_source_resolve_hg.cmake index 3a1ac9fc..8505f516 100644 --- a/cmake/package/package_source/hg/package_source_resolve_hg.cmake +++ b/cmake/package/package_source/hg/package_source_resolve_hg.cmake @@ -37,20 +37,4 @@ return_ref(package_handle) - - - -return() - file_tempdir() - ans(temp_dir) - - package_source_pull_hg("${uri}" "${temp_dir}") - ans(package_handle) - - if(NOT package_handle) - return() - endif() - - - return_ref(package_handle) endfunction() diff --git a/cmake/package/package_source/managed/managed_package_source.cmake b/cmake/package/package_source/managed/managed_package_source.cmake index 937d0898..f781d955 100644 --- a/cmake/package/package_source/managed/managed_package_source.cmake +++ b/cmake/package/package_source/managed/managed_package_source.cmake @@ -7,7 +7,8 @@ pull:'package_source_pull_managed', push:'package_source_push_managed', query:'package_source_query_managed', - resolve:'package_source_resolve_managed' + resolve:'package_source_resolve_managed', + delete:'package_source_delete_managed' }") return_ans() endfunction() diff --git a/cmake/package/package_source/managed/package_source_delete_managed.cmake b/cmake/package/package_source/managed/package_source_delete_managed.cmake new file mode 100644 index 00000000..bbc21e81 --- /dev/null +++ b/cmake/package/package_source/managed/package_source_delete_managed.cmake @@ -0,0 +1,20 @@ +function(package_source_delete_managed uri) + uri_coerce(uri) + + package_source_resolve_managed("${uri}") + ans(package_handle) + + + if(NOT package_handle) + return(false) + endif() + + assign(location = package_handle.managed_descriptor.managed_dir) + if(NOT EXISTS "${location}") + message(FATAL_ERROR "the package is known but its directory does not exist") + endif() + + rm(-r "${location}") + + return(true) +endfunction() \ No newline at end of file diff --git a/cmake/package/package_source/managed/package_source_pull_managed.cmake b/cmake/package/package_source/managed/package_source_pull_managed.cmake index b7cd53a1..421eb092 100644 --- a/cmake/package/package_source/managed/package_source_pull_managed.cmake +++ b/cmake/package/package_source/managed/package_source_pull_managed.cmake @@ -3,6 +3,9 @@ ## --reference returns the package with the content still pointing to the original content dir function(package_source_pull_managed uri) set(args ${ARGN}) + + uri_coerce(uri) + package_source_resolve_managed("${uri}") ans(package_handle) if(NOT package_handle) @@ -12,9 +15,6 @@ list_extract_flag(args --reference) ans(reference) - ## remove index field as it is not of interest to the client - map_remove(${package_handle} index) - ## if in reference mode copy package_handle content and set new content_dir if(NOT reference) diff --git a/cmake/package/package_source/managed/package_source_push_managed.cmake b/cmake/package/package_source/managed/package_source_push_managed.cmake index 45918206..e09d4019 100644 --- a/cmake/package/package_source/managed/package_source_push_managed.cmake +++ b/cmake/package/package_source/managed/package_source_push_managed.cmake @@ -7,71 +7,61 @@ ## --reference flag indicates that the content will not be copied into the the package source ## the already existing package dir will be used ## --force flag indicates that existing package should be overwritten - function(package_source_push_managed package_handle) - set(args ${ARGN}) + function(package_source_push_managed) + if("${ARGN}" MATCHES "(.*);=>;?(.*)") + set(source_args "${CMAKE_MATCH_1}") + set(args "${CMAKE_MATCH_2}") + else() + set(source_args ${ARGN}) + set(args) + endif() + list_pop_front(source_args) + ans(source) + - list_extract_flag(args --reference) - ans(reference) + list_extract_flag(args --force) + ans(force) this_get(directory) this_get(source_name) - ## check if package handle is valid - package_handle("${package_handle}") - ans(package_handle) + list_peek_front(source_args) + ans(uri) + + assign(package_handle = source.resolve(${uri})) if(NOT package_handle) + error("could not result ${source_args} to a package handle") return() endif() - ## create a hash for the package package_handle_hash("${package_handle}") ans(hash) set(location "${directory}/${hash}") - if(EXISTS "${location}") - ## same hash already used + if(EXISTS "${location}" AND NOT force) + error("package (${hash}) already exists ") return() endif() - map_tryget(${package_handle} content_dir) - ans(source_content_dir) - - set(content_dir "${location}/content") + set(target_dir "${location}/content") + + assign(package_handle = source.pull(${source_args} "${target_dir}")) + + assign(!package_handle.managed_descriptor.hash = hash) + assign(!package_handle.managed_descriptor.managed_dir = location) + assign(!package_handle.working_dir = '${location}/workspace') + assign(!package_handle.managed_descriptor.source_name = source_name) + assign(!package_handle.content_dir = target_dir) + + qm_write("${location}/package_handle.qm" "${package_handle}") + + + return_ref(package_handle) - ## if reference do not copy content else copy content - if(NOT reference) - ## copy only if exists (if it does not exist no content dir is set) - cp_dir("${source_content_dir}" "${location}/content") - else() - set(content_dir "${source_content_dir}") - endif() - ## set local urio - set(local_uri "${source_name}:${hash}") - - set(index) - - # create a index file which contains all searchable data - assign(!index.hash = hash) - assign(!index.id = package_handle.package_descriptor.id) - assign(!index.version = package_handle.package_descriptor.id) - assign(!index.tags = package_handle.package_descriptor.tags) - assign(!index.tags[] = package_handle.package_descriptor.id) - assign(!index.local_uri = local_uri) - assign(!index.remote_uri = package_handle.uri) - assign(!index.remote_query_uri = package_handle.query_uri) - assign(!index.content_dir = content_dir) - assign(!index.source_content_dir = source_content_dir) - qm_write("${location}/index.cmake" "${index}") - - assign(package_descriptor = package_handle.package_descriptor) - qm_write("${location}/package.cmake" "${package_descriptor}") - - - return_ref(local_uri) endfunction() diff --git a/cmake/package/package_source/managed/package_source_query_managed.cmake b/cmake/package/package_source/managed/package_source_query_managed.cmake index bedc72f2..3fde7c60 100644 --- a/cmake/package/package_source/managed/package_source_query_managed.cmake +++ b/cmake/package/package_source/managed/package_source_query_managed.cmake @@ -12,101 +12,38 @@ this_get(directory) this_get(source_name) - uri("${uri}") - ans(uri) - - assign(scheme = uri.scheme) - if(NOT "${scheme}_" STREQUAL "_" AND NOT "${scheme}" STREQUAL "${source_name}") - return() - endif() - - map_tryget(${uri} segments) - ans(segments) - list(LENGTH segments segment_length) - - - ## if uri has a single segment it is interpreted as a hash - if(segment_length EQUAL 1 AND IS_DIRECTORY "${directory}/${segments}") - #set(result "${source_name}:${segments}") - qm_read("${directory}/${segments}/index.cmake") - ans(result) - elseif(NOT segment_length EQUAL 0) - ## multiple segments are not allowed and are a invliad uri - set(result) - else() - ## else parse uri's query (uri starts with ?) - - map_tryget(${uri} query) - ans(query) - if("${query}" MATCHES "=") - ## if query contains an equals it is a map - ## else it is a value - map_tryget(${uri} params) - ans(query) - endif() - - ## empty query returns nothing - if("${query}_" STREQUAL "_") - return() - endif() - - ## read all package indices - file(GLOB index_files "${directory}/*/index.cmake") - - ## parse index_files - set(indices) - foreach(index ${index_files}) - qm_read("${index}") - ans(package) - list(APPEND indices "${package}") + uri_coerce(uri) + + ### read all package handles (new objects) + ### also set the query_uri field + file(GLOB package_handle_files "${directory}/*/package_handle.qm") + set(package_handles) + + ## this is slow and may be made faster + foreach(package_handle_file ${package_handle_files}) + qm_read("${package_handle_file}") + ans(package_handle) + assign(package_handle.query_uri = uri.uri) + list(APPEND package_handles ${package_handle}) + endforeach() + + + ## filter package handles by query + package_handle_filter(package_handles "${uri}") + ans(filtered_handles) + + if(NOT return_package_handle) + set(package_uris) + foreach(package_handle ${filtered_handles}) + map_tryget(${package_handle} uri) + ans(uri) + list(APPEND package_uris ${uri}) endforeach() - - - map_isvalid("${query}") - ans(ismap) - - ## query may be a * which returns all packages - ## or a regex /[regex]/ - ## or a map which will uses the properties to match values - if(query STREQUAL "*") - set(result ${indices}) - #list_select_property(indices local_uri) - #ans(result) - elseif("${query}" MATCHES "^/(.*)/$") - set(regex "${CMAKE_MATCH_1}") - set(result) - foreach(index ${indices}) - map_tryget(${index} hash) - ans(hash) - if("${hash}" MATCHES ${regex}) - list(APPEND result ${index}) - #list(APPEND result "${source_name}:${hash}") - endif() - endforeach() - elseif(ismap) - ## todo - endif() - - endif() - - - if(return_package_handle) - set(package_handles) - foreach(index ${result}) - set(package_handle) - assign(!package_handle.uri = index.local_uri) - assign(!package_handle.query_uri = uri.uri) - assign(!package_handle.managed_descriptor.index = index) - assign(!package_handle.managed_descriptor.source = this) - - list(APPEND package_handles ${package_handle}) - endforeach() - set(result ${package_handles}) + return_ref(package_uris) else() - list_select_property(result local_uri) - ans(result) - endif() + return_ref(filtered_handles) + endif() + - ## return uris - return_ref(result) + return() endfunction() \ No newline at end of file diff --git a/cmake/package/package_source/managed/package_source_resolve_managed.cmake b/cmake/package/package_source/managed/package_source_resolve_managed.cmake index 22175e40..8de5171b 100644 --- a/cmake/package/package_source/managed/package_source_resolve_managed.cmake +++ b/cmake/package/package_source/managed/package_source_resolve_managed.cmake @@ -3,61 +3,22 @@ ## expects a var called this exist which contains the properties 'directory' and 'source_name' ## function(package_source_resolve_managed uri) - ## query for package uri - package_source_query_managed("${uri}") - ans(valid_uri_string) + uri_coerce(uri) - list(LENGTH valid_uri_string count) - if(NOT count EQUAL 1) - return() - endif() + ## query for package uri + package_source_query_managed("${uri}" --package-handle) + ans(package_handle) - ## if uri contains query return - if("${uri}" MATCHES "\\?") + + list(LENGTH package_handle count) + if(NOT "${count}" EQUAL 1) return() endif() + return_ref(package_handle) - this_get(directory) - - ## parse uri - uri("${valid_uri_string}") - ans(uri) - - ## the scheme specific part is the hash (ie everything but the scheme) - map_tryget(${uri} scheme_specific_part) - ans(hash) - set(managed_dir "${directory}/${hash}") - - ## read the index file in the correct folder (hash) - ## if none exists then the uri is invalid - qm_read("${managed_dir}/index.cmake") - ans(index) - - if(NOT index) - return() - endif() - - ## read the package descriptor which is stored alongside the index - qm_read("${managed_dir}/package.cmake") - ans(package_descriptor) - if(NOT package_descriptor) - return() - endif() + return() - ## get content dir from index (might not be a subdir if push --reference is used) - map_tryget(${index} content_dir) - ans(content_dir) - - ## generate response - map_new() - ans(response) - map_set(${response} package_descriptor "${package_descriptor}") - map_set(${response} uri "${valid_uri_string}") - map_set(${response} content_dir "${content_dir}") - map_set(${response} managed_dir "${managed_dir}") - map_set(${response} index "${index}") ## also store the optional index - return_ref(response) endfunction() \ No newline at end of file diff --git a/cmake/package/package_source/package_source_best_match.cmake b/cmake/package/package_source/package_source_best_match.cmake index 9f70a09e..d6dcd9d4 100644 --- a/cmake/package/package_source/package_source_best_match.cmake +++ b/cmake/package/package_source/package_source_best_match.cmake @@ -3,7 +3,7 @@ uri("${uri}") ans(uri) - list_to_map(${__lst} "(m)->map_tryget($m source_name)") + list_to_map(${__lst} "[](m)map_tryget({{m}} source_name)") ans(map) map_tryget("${uri}" schemes) diff --git a/cmake/package/package_source/package_source_transfer.cmake b/cmake/package/package_source/package_source_transfer.cmake new file mode 100644 index 00000000..88d0db9c --- /dev/null +++ b/cmake/package/package_source/package_source_transfer.cmake @@ -0,0 +1,17 @@ +function(package_source_transfer) + if("${ARGN}" MATCHES "(.*)=>(.*)") + set(source_args ${CMAKE_MATCH_1}) + set(sink_args ${CMAKE_MATCH_2}) + else() + message(FATAL_ERROR "invalid arguments. expcted => ") + endif() + list_pop_front(source_args) + ans(source) + + list_pop_front(sink_args) + ans(sink) + + assign(package_handle = sink.push(${source} ${source_args} => ${sink_args})) + + return_ref(package_handle) +endfunction() diff --git a/cmake/package/package_source/path/package_source_push_path.cmake b/cmake/package/package_source/path/package_source_push_path.cmake index 4d643164..ab403a5d 100644 --- a/cmake/package/package_source/path/package_source_push_path.cmake +++ b/cmake/package/package_source/path/package_source_push_path.cmake @@ -1,47 +1,39 @@ -## package_source_push_path( <~uri> ?>) - function(package_source_push_path package_handle uri) - set(args ${ARGN}) - - ## resolve installed package - package_handle("${package_handle}") - ans(package_handle) - - if(NOT package_handle) - return() +## ( <~uri> [--reference] [--consume] ?>) +## +function(package_source_push_path) + if("${ARGN}" MATCHES "(.*);=>;?(.*)") + set(source_args "${CMAKE_MATCH_1}") + set(args "${CMAKE_MATCH_2}") + else() + set(source_args ${ARGN}) + set(args) endif() + list_pop_front(source_args) + ans(source) + - ## get package_descriptor and source_dir from package_handle - map_tryget(${package_handle} package_descriptor) - ans(package_descriptor) - - map_tryget(${package_handle} content_dir) - ans(source_dir) - - ## get target_dir - uri("${uri}") - ans(uri) - - uri_to_localpath("${uri}") + ## get target dir + list_pop_front(args) ans(target_dir) + if(NOT target_dir) + pwd() + ans(target_dir) + endif() path_qualify(target_dir) - - - ## copy content to target dir - map_tryget("${package_descriptor}" content) - ans(content_globbing_expression) - cp_content("${source_dir}" "${target_dir}" ${content_globbing_expression}) - ans(result) - json_write("${target_dir}/package.cmake" "${package_descriptor}") + assign(package_handle = source.pull(${source_args} "${target_dir}")) - ## return the valid target uri - uri("${target_dir}") - ans(target_uri) + if(NOT package_handle) + error("could not pull `${source_args}` ") + return() + endif() - uri_format(${target_uri}) - ans(target_uri) + if(NOT EXISTS "${target_dir}/package.cmake") + assign(package_descriptor = package_handle.package_descriptor) + json_write("${target_dir}/package.cmake" "${package_descriptor}") + endif() - return_ref(target_uri) - endfunction() \ No newline at end of file + return_ref(package_handle) +endfunction() \ No newline at end of file diff --git a/cmake/package/package_source/path/path_package_source.cmake b/cmake/package/package_source/path/path_package_source.cmake index 7cd29a2e..b52e68ba 100644 --- a/cmake/package/package_source/path/path_package_source.cmake +++ b/cmake/package/package_source/path/path_package_source.cmake @@ -3,6 +3,7 @@ obj("{ source_name:'file', pull:'package_source_pull_path', + push:'package_source_push_path', query:'package_source_query_path', resolve:'package_source_resolve_path' }") diff --git a/cmake/package/package_source/svn/package_source_resolve_svn.cmake b/cmake/package/package_source/svn/package_source_resolve_svn.cmake index 0c5d2876..50b50ace 100644 --- a/cmake/package/package_source/svn/package_source_resolve_svn.cmake +++ b/cmake/package/package_source/svn/package_source_resolve_svn.cmake @@ -31,7 +31,7 @@ file_make_temporary("") ans(tmp) rm(${tmp}) - svn(export "${checkout_uri}" "${tmp}" --return-code) + svn(export "${checkout_uri}" "${tmp}" --exit-code) ans(error) if(NOT error) diff --git a/cmake/package/package_source/webarchive/package_source_resolve_webarchive.cmake b/cmake/package/package_source/webarchive/package_source_resolve_webarchive.cmake index d5c9f876..17e1afbe 100644 --- a/cmake/package/package_source/webarchive/package_source_resolve_webarchive.cmake +++ b/cmake/package/package_source/webarchive/package_source_resolve_webarchive.cmake @@ -25,10 +25,8 @@ error("could not download {resource_uri}" --aftereffect) return() endif() - package_source_resolve_archive("${cached_archive_path}") ans(archive_package_handle) - if(NOT archive_package_handle) error("{uri.uri} is not a supported archive file ") return() diff --git a/cmake/package/project/project_install.cmake b/cmake/package/project/project_install.cmake index 9c9cb84e..f6b66cf3 100644 --- a/cmake/package/project/project_install.cmake +++ b/cmake/package/project/project_install.cmake @@ -9,43 +9,34 @@ ans(uri) if(NOT uri) error("no uri was specified to install") + return() endif() - uri("${uri}") - ans(uri) + uri_coerce(uri) + ## pull package from remote source to temp directory - ## then push it into dependency_source from there + ## then push it into local from there ## return if anything did not work - path_temp() - ans(temp_dir) - assign(project_dir = this.project_dir) - assign(remote_package = this.remote.pull("${uri}" "${temp_dir}" ${args})) - if(NOT remote_package) - rm("${temp_dir}") - error("remote package could not be pulled: '{uri.input}'" uri temp_dir) - return() - endif() - assign(package_uri = this.dependency_source.push("${remote_package}" ${args})) - rm("${temp_dir}") - if(NOT package_uri) - error("remote package could not pushed into project: '{uri.input}'" uri remote_package) - return() - endif() - assign(installed_package_handle = this.dependency_source.resolve("${package_uri}")) + assign(remote = this.remote) + assign(local = this.local) + + + + assign(installed_package_handle = local.push(${remote} ${uri})) + if(NOT installed_package_handle) - print_vars(package_uri) message(FATAL_ERROR "nononono") endif() + ## project install is executed before load project_install_package("${installed_package_handle}") ## project is loaded project_load_installed_package("${installed_package_handle}") - - return_ref(package_uri) + return_ref(installed_package_handle) endfunction() \ No newline at end of file diff --git a/cmake/package/project/project_load.cmake b/cmake/package/project/project_load.cmake index 4dc0e1c0..ed572f9f 100644 --- a/cmake/package/project/project_load.cmake +++ b/cmake/package/project/project_load.cmake @@ -53,8 +53,8 @@ function(project_load) ## create package source for project managed_package_source("project" "${dependency_dir}") - ans(dependency_source) - assign(this.dependency_source = dependency_source) + ans(local) + assign(this.local = local) ## load all installed packages ## including this project diff --git a/cmake/package/project/project_load_installed_packages.cmake b/cmake/package/project/project_load_installed_packages.cmake index 040c1167..755d08a7 100644 --- a/cmake/package/project/project_load_installed_packages.cmake +++ b/cmake/package/project/project_load_installed_packages.cmake @@ -7,13 +7,13 @@ ## project_on_packages_loaded() function(project_load_installed_packages) ## load all packages - assign(installed_package_uris = this.dependency_source.query("?*")) - + assign(installed_package_uris = this.local.query("?*")) set(package_handles) foreach(installed_package_uri ${installed_package_uris}) - assign(package_handle = this.dependency_source.resolve("${installed_package_uri}")) + assign(package_handle = this.local.resolve("${installed_package_uri}")) list(APPEND package_handles ${package_handle}) assign(success = project_load_installed_package("${package_handle}")) + assign(this.installed_package_handles[] = ${package_handle}) endforeach() ## lastly load the current project diff --git a/cmake/package/project/project_new.cmake b/cmake/package/project/project_new.cmake index 305d9917..6cc25f93 100644 --- a/cmake/package/project/project_new.cmake +++ b/cmake/package/project/project_new.cmake @@ -5,7 +5,7 @@ ## packages ## ## it has a remote package source which is queried to install packages - ## and a local managed package source (dependency_source) which manages + ## and a local managed package source (local) which manages ## installed packages ## function(project_new) diff --git a/cmake/package/project/project_save.cmake b/cmake/package/project/project_save.cmake index a8fb992a..4e251aed 100644 --- a/cmake/package/project/project_save.cmake +++ b/cmake/package/project/project_save.cmake @@ -23,6 +23,8 @@ function(project_save) endif() + project_save_installed_packages() + ## save package descriptor assign(package_descriptor_file = this.configuration.package_descriptor_file) path_qualify_from("${project_dir}" "${package_descriptor_file}") diff --git a/cmake/package/project/project_save_installed_package.cmake b/cmake/package/project/project_save_installed_package.cmake new file mode 100644 index 00000000..08f56ea3 --- /dev/null +++ b/cmake/package/project/project_save_installed_package.cmake @@ -0,0 +1,4 @@ + +function(project_save_installed_package installed_package_handle) + event_emit(project_on_installed_package_save ${this} ${installed_package_handle}) +endfunction() \ No newline at end of file diff --git a/cmake/package/project/project_save_installed_packages.cmake b/cmake/package/project/project_save_installed_packages.cmake new file mode 100644 index 00000000..c7136fef --- /dev/null +++ b/cmake/package/project/project_save_installed_packages.cmake @@ -0,0 +1,10 @@ +function(project_save_installed_packages) + assign(installed_package_handles = this.installed_package_handles) + + foreach(installed_package_handle ${installed_package_handles} ${this}) + project_save_installed_package(${installed_package_handle}) + endforeach() + + event_emit(project_on_installed_packages_save ${this}) +endfunction() + diff --git a/cmake/package/project/project_uninstall.cmake b/cmake/package/project/project_uninstall.cmake index be3eaf36..1bd33994 100644 --- a/cmake/package/project/project_uninstall.cmake +++ b/cmake/package/project/project_uninstall.cmake @@ -4,10 +4,9 @@ ## events: ## project_on_package_uninstall( ) function(project_uninstall uri) - uri("${uri}") - ans(uri) + uri_coerce(uri) - assign(installed_package = this.dependency_source.resolve("${uri}")) + assign(installed_package = this.local.resolve("${uri}")) if(NOT installed_package) error("package '{uri.input}' does not exist in project") @@ -16,8 +15,9 @@ event_emit(project_on_package_uninstall ${this} ${installed_package}) - map_import_properties(${installed_package} managed_dir) - rm("${managed_dir}") + assign(package_uri = installed_package.uri) + assign(success = this.local.delete("${package_uri}")) - return(true) + + return(${success}) endfunction() \ No newline at end of file diff --git a/cmake/parser/parse_match.cmake b/cmake/parser/parse_match.cmake index 0323c41d..7a2143fe 100644 --- a/cmake/parser/parse_match.cmake +++ b/cmake/parser/parse_match.cmake @@ -6,10 +6,10 @@ map_get(${definition} search) ans(search) - #message("parsing match with '${parser_id}' (search: '${search}') for '${str}'") + # message("parsing match with '${parser_id}' (search: '${search}') for '${str}'") map_tryget(${definition} ignore_regex) ans(ignore_regex) - # message("ignore: ${ignore_regex}") + #message("ignore: ${ignore_regex}") list(LENGTH ignore_regex len) if(len) # message("ignoring ${ignore_regex}") diff --git a/cmake/parser/parse_string.cmake b/cmake/parser/parse_string.cmake index d4f2ffa8..b82b1ac4 100644 --- a/cmake/parser/parse_string.cmake +++ b/cmake/parser/parse_string.cmake @@ -16,12 +16,12 @@ ans(parser_id) # - #message(FORMAT "${parser_id} parser parsing ${definition_id}..") - #message_indent_push() + # message(FORMAT "${parser_id} parser parsing ${definition_id}..") + message_indent_push() __call_string_parser("${parser_id}" "${rstring}") ans(res) - #message_indent_pop() - #message(FORMAT "${parser_id} parser returned: ${res} rest is ") + message_indent_pop() + # message(FORMAT "${parser_id} parser returned: ${res} rest is") #list(LENGTH res len) # if(len) # message("parsed '${res}' with ${parser_id} parser") diff --git a/cmake/persistence/file_data/file_data_dir.cmake b/cmake/persistence/file_data/file_data_dir.cmake index 357adb45..e1231ccd 100644 --- a/cmake/persistence/file_data/file_data_dir.cmake +++ b/cmake/persistence/file_data/file_data_dir.cmake @@ -3,7 +3,7 @@ function(file_data_ids dir) path("${dir}") ans(dir) - file_glob("${dir}" *.cmake) + glob("${dir}/*.cmake") ans(files) set(keys) diff --git a/cmake/persistence/user_data/user_data_dir.cmake b/cmake/persistence/user_data/user_data_dir.cmake index 21d9ca37..1a438735 100644 --- a/cmake/persistence/user_data/user_data_dir.cmake +++ b/cmake/persistence/user_data/user_data_dir.cmake @@ -1,9 +1,9 @@ ## returns the where the user data is stored -# this is the home dir/.oocmake +# this is the home dir/.cmakepp function(user_data_dir) home_dir() ans(home_dir) - set(storage_dir "${home_dir}/.oocmake") + set(storage_dir "${home_dir}/.cmakepp") if(NOT EXISTS "${storage_dir}") mkdir("${storage_dir}") endif() diff --git a/cmake/persistence/user_data/user_data_ids.cmake b/cmake/persistence/user_data/user_data_ids.cmake index 7b24da83..eefb0d1a 100644 --- a/cmake/persistence/user_data/user_data_ids.cmake +++ b/cmake/persistence/user_data/user_data_ids.cmake @@ -2,7 +2,7 @@ function(user_data_ids) user_data_dir() ans(dir) - file_glob("${dir}" *.cmake) + glob("${dir}/*.cmake") ans(files) set(keys) foreach(file ${files}) diff --git a/cmake/persistence/user_data/user_data_path.cmake b/cmake/persistence/user_data/user_data_path.cmake index 3f65bb5a..a7a5e84b 100644 --- a/cmake/persistence/user_data/user_data_path.cmake +++ b/cmake/persistence/user_data/user_data_path.cmake @@ -1,6 +1,6 @@ ## returns the user data path for the specified id ## id can be any string that is also usable as a valid filename -## it is located in %HOME_DIR%/.oocmake +## it is located in %HOME_DIR%/.cmakepp function(user_data_path id) if(NOT id) message(FATAL_ERROR "no id specified") diff --git a/cmake/persistence/user_data/user_data_set.cmake b/cmake/persistence/user_data/user_data_set.cmake index fe5d3582..2494053a 100644 --- a/cmake/persistence/user_data/user_data_set.cmake +++ b/cmake/persistence/user_data/user_data_set.cmake @@ -1,10 +1,10 @@ ## sets and persists data for the current user specified by identified by ## nav can be empty or a "." which will set the data at the root level ## else it can be a navigation expressions which (see map_navigate_set) -## e.g. user_data_set(common_directories oocmake.base_dir /home/user/mydir) +## e.g. user_data_set(common_directories cmakepp.base_dir /home/user/mydir) ## results in common_directories to contain ## { -## oocmake:{ +## cmakepp:{ ## base_dir:"/home/user/mydir" ## } ## } diff --git a/cmake/process/README.md b/cmake/process/README.md index 679492a3..207fa3d6 100644 --- a/cmake/process/README.md +++ b/cmake/process/README.md @@ -1,9 +1,48 @@ -## Process Management +# Process Management This section how to manage processes and external programs. Besides replacements for cmake's `execute_process` function this section defines a control system for parallel processes controlled from any cmake. +## Function List + + +* [async](#async) +* [await](#await) +* [command_line](#command_line) +* [command_line_args_combine](#command_line_args_combine) +* [command_line_args_escape](#command_line_args_escape) +* [command_line_parse](#command_line_parse) +* [command_line_parse_string](#command_line_parse_string) +* [command_line_to_string](#command_line_to_string) +* [execute](#execute) +* [process_execute](#process_execute) +* [process_handle](#process_handle) +* [process_handles](#process_handles) +* [process_handle_change_state](#process_handle_change_state) +* [process_handle_get](#process_handle_get) +* [process_handle_new](#process_handle_new) +* [process_handle_register](#process_handle_register) +* [process_info](#process_info) +* [process_isrunning](#process_isrunning) +* [process_kill](#process_kill) +* [process_list](#process_list) +* [process_refresh_handle](#process_refresh_handle) +* [process_return_code](#process_return_code) +* [process_start](#process_start) +* [](#) +* [process_start_info_new](#process_start_info_new) +* [process_start_script](#process_start_script) +* [process_stderr](#process_stderr) +* [process_stdout](#process_stdout) +* [process_timeout](#process_timeout) +* [process_wait](#process_wait) +* [process_wait_all](#process_wait_all) +* [process_wait_any](#process_wait_any) +* [string_take_commandline_arg](#string_take_commandline_arg) +* [wrap_executable](#wrap_executable) + + ## Common Definitions The following definitions are common to the following subsections. @@ -39,7 +78,7 @@ Another example showing usage of the `execute()` function: ``` find_package(Hg) set(cmdline --version) -execute({path:$HG_EXECUTABLE, args: $cmdline} --result) +execute({path:$HG_EXECUTABLE, args: $cmdline} --process-handle) ans(res) map_get(${res} result) ans(error) @@ -53,7 +92,7 @@ json_print(${res}) # outputs input and output of process ### Functions and Datatypes -* `execute( [--result|--return-code]) -> ||` executes the process described by `` and by default fails fatally if return code is not 0. if `--result` flag is specified `` is returned and if `` is specified the command's return code is returned. (the second two options will not cause a fatal error) +* `execute( [--process-handle|--exit-code]) -> ||` executes the process described by `` and by default fails fatally if return code is not 0. if `--exit-code` flag is specified `` is returned and if `` is specified the command's return code is returned. (the second two options will not cause a fatal error) * example: `execute("{path:'', args:['a','b']}")` * `wrap_executable( )` takes the executable/command and wraps it inside a function called `` it has the same signature as `execute(...)` * `` a string which can be converted to a `` object using the `process_start_info()` function. @@ -220,39 +259,8 @@ To communicate with you processes you can use any of the following well known me -### Function List - - -* [async](#async) -* [await](#await) -* [command_line](#command_line) -* [command_line_args_combine](#command_line_args_combine) -* [command_line_args_escape](#command_line_args_escape) -* [command_line_parse](#command_line_parse) -* [command_line_parse_string](#command_line_parse_string) -* [command_line_to_string](#command_line_to_string) -* [process_handle](#process_handle) -* [process_handles](#process_handles) -* [process_info](#process_info) -* [process_isrunning](#process_isrunning) -* [process_kill](#process_kill) -* [process_list](#process_list) -* [process_refresh_handle](#process_refresh_handle) -* [process_return_code](#process_return_code) -* [process_start](#process_start) -* [](#) -* [process_start_script](#process_start_script) -* [process_stderr](#process_stderr) -* [process_stdout](#process_stdout) -* [process_timeout](#process_timeout) -* [process_wait](#process_wait) -* [process_wait_all](#process_wait_all) -* [process_wait_any](#process_wait_any) -* [test](#test) -* [string_take_commandline_arg](#string_take_commandline_arg) -* [wrap_executable](#wrap_executable) -### Function Descriptions +## Function Descriptions ## `async` @@ -320,6 +328,60 @@ To communicate with you processes you can use any of the following well known me +## `execute` + + `( [--process-handle] [--exit-code] [--async] [--silent-fail] [--success-callback ] [--error-callback ] [--state-changed-callback ])->|||` + + *options* + * `--process-handle` + * `--exit-code` + * `--async` + * `--silent-fail` + * `--success-callback [exit_code]()` + * `--error-callback [exit_code]()` + * `--state-changed-callback [old_state\;new_state]()` + + *example* + ``` + execute(cmake -E echo_append hello) -> 'hello' + ``` + + + + +## `process_execute` + + `()->` + + executes the specified command with the specified arguments in the + current working directory + creates and registers a process handle which is then returned + this function accepts arguments as encoded lists. this allows you to include + arguments which contain semicolon and other special string chars. + the process id of processes start with `process_execute` is always -1 + because `CMake`'s `execute_process` does not return it. This is not too much of a problem + because the process will always be terminated as soon as the function returns + + **parameters** + * `` the executable (may contain spaces) + * `` the arguments - may be an encoded list + **scope** + * `pwd()` used as the working-directory + **events** + * `on_process_handle_created` global event is emitted when the process_handle is ready + * `process_handle.on_state_changed` + + **returns** + ``` + ::= { + pid: "-1"|"0" + + } + ``` + + + + ## `process_handle` returns the runtime unique process handle @@ -337,6 +399,46 @@ To communicate with you processes you can use any of the following well known me +## `process_handle_change_state` + + + + + +## `process_handle_get` + + + + + +## `process_handle_new` + + `()->` + + returns a new process handle which has the following layout: + ``` + ::= { + pid: + start_info: + state: "unknown"|"running"|"terminated" + stdout: + stderr: + exit_code: | + command: + command_args: + on_state_change: [old_state, new_state](${process_handle}) + } + ``` + + + + +## `process_handle_register` + + + + + ## `process_info` process_info(): @@ -397,6 +499,36 @@ To communicate with you processes you can use any of the following well known me +## `process_start_info_new` + + `(|)->` + ` ::= "COMMAND"? ` + + creates a new process_start_info with the following fields + ``` + ::= { + command: + command_arguments: + working_directory: + timeout: + } + ``` + + *example* + * `process_start_info_new(COMMAND cmake -E echo "asd bsd" csd) -> { + "command":"cmake", + "command_arguments":[ + "-E", + "echo", + "asdbsd" + ], + "working_directory":"C:/Users/Tobi/Documents/projects/cmakepp/cmake/process", + "timeout":"-1" +}` + + + + ## `process_start_script` shorthand to fork a cmake script @@ -440,25 +572,35 @@ To communicate with you processes you can use any of the following well known me ## `process_wait_all` - process_wait_all(> >) - waits for all specified to finish - specify --quietly to supress output - if --timeout is specified the function will return all finished processes after n seconds + `(> [--timeout ] [--idle-callback ] [--task-complete-callback ] )` + waits for all specified to finish returns them in the order + in which they completed + `--timeout ` if value is specified the function will return all + finished processes after n seconds + `--idle-callback ` + if value is specified it will be called at least once + and between every query if a task is still running -## `process_wait_any` + `--task-complete-callback ` + if value is specified it will be called whenever a + task completes. - waits until any of the specified handles stops running - returns the handle of that process - if --timeout is specified function will return nothing after n seconds + *Example* + `process_wait_all(${handle1} ${handle1} --task-complete-callback "[](handle)message(FORMAT '{handle.pid}')")` + prints the process id to the console whenver a process finishes -## `test` +## `process_wait_any` + + waits until any of the specified handles stops running + returns the handle of that process + if --timeout is specified function will return nothing after n seconds diff --git a/cmake/process/README.md.in b/cmake/process/README.md.in index 79fc9aa3..cf65b771 100644 --- a/cmake/process/README.md.in +++ b/cmake/process/README.md.in @@ -1,4 +1,4 @@ -## Process Management +# Process Management <% assign(function_files = glob("**.cmake" --relative)) %> @@ -6,6 +6,11 @@ This section how to manage processes and external programs. Besides replacements for cmake's `execute_process` function this section defines a control system for parallel processes controlled from any cmake. +## Function List + +<%= markdown_template_function_list(${function_files}) %> + + ## Common Definitions The following definitions are common to the following subsections. @@ -41,7 +46,7 @@ Another example showing usage of the `execute()` function: ``` find_package(Hg) set(cmdline --version) -execute({path:$HG_EXECUTABLE, args: $cmdline} --result) +execute({path:$HG_EXECUTABLE, args: $cmdline} --process-handle) ans(res) map_get(${res} result) ans(error) @@ -55,7 +60,7 @@ json_print(${res}) # outputs input and output of process ### Functions and Datatypes -* `execute( [--result|--return-code]) -> ||` executes the process described by `` and by default fails fatally if return code is not 0. if `--result` flag is specified `` is returned and if `` is specified the command's return code is returned. (the second two options will not cause a fatal error) +* `execute( [--process-handle|--exit-code]) -> ||` executes the process described by `` and by default fails fatally if return code is not 0. if `--exit-code` flag is specified `` is returned and if `` is specified the command's return code is returned. (the second two options will not cause a fatal error) * example: `execute("{path:'', args:['a','b']}")` * `wrap_executable( )` takes the executable/command and wraps it inside a function called `` it has the same signature as `execute(...)` * `` a string which can be converted to a `` object using the `process_start_info()` function. @@ -222,11 +227,8 @@ To communicate with you processes you can use any of the following well known me -### Function List - -<%= markdown_template_function_list(${function_files}) %> -### Function Descriptions +## Function Descriptions <%= markdown_template_function_descriptions(${function_files}) %> diff --git a/cmake/process/async.cmake b/cmake/process/async.cmake index 7cb7cf72..364515d2 100644 --- a/cmake/process/async.cmake +++ b/cmake/process/async.cmake @@ -7,7 +7,7 @@ ## include further files for custom functions ## environment vars function(async callable) - oocmake_config(base_dir) + cmakepp_config(base_dir) ans(base_dir) set(args ${ARGN}) list_pop_front(args) diff --git a/cmake/process/command_line_args_combine.cmake b/cmake/process/command_line_args_combine.cmake index 0fbe67d8..43d9aa01 100644 --- a/cmake/process/command_line_args_combine.cmake +++ b/cmake/process/command_line_args_combine.cmake @@ -4,8 +4,7 @@ ans(args) string_combine(" " ${args}) ans(res) - string_semicolon_decode("${res}") - ans(res) - + string_decode_list("${res}") + ans(res) return_ref(res) endfunction() diff --git a/cmake/process/execute.cmake b/cmake/process/execute.cmake new file mode 100644 index 00000000..39ccd626 --- /dev/null +++ b/cmake/process/execute.cmake @@ -0,0 +1,113 @@ +## `( [--process-handle] [--exit-code] [--async] [--silent-fail] [--success-callback ] [--error-callback ] [--state-changed-callback ])->|||` +## +## *options* +## * `--process-handle` +## * `--exit-code` +## * `--async` +## * `--silent-fail` +## * `--success-callback [exit_code]()` +## * `--error-callback [exit_code]()` +## * `--state-changed-callback [old_state;new_state]()` +## +## *example* +## ``` +## execute(cmake -E echo_append hello) -> '@execute(cmake -E echo_append hello)' +## ``` +function(execute) + arguments_encoded_list(${ARGC}) + ans(args) + + list_extract_flag(args --process-handle) + ans(return_handle) + list_extract_flag(args --exit-code) + ans(return_exit_code) + list_extract_flag(args --async) + ans(async) + #list_extract_flag(args --async-wait) + #ans(wait) + #if(wait) + # set(async true) + #endif() + list_extract_flag(args --silent-fail) + ans(silent_fail) + + list_extract_labelled_value(args --success-callback) + ans(success_callback) + list_extract_labelled_value(args --error-callback) + ans(error_callback) + list_extract_labelled_value(args --state-changed-callback) + ans(process_callback) + + if(NOT args) + messagE(FATAL_ERROR "no command specified") + endif() + + process_start_info_new(${args}) + ans(start_info) + + +##debug here + #print_vars(start_info.command start_info.command_arguments) + + process_handle_new(${start_info}) + ans(process_handle) + + if(success_callback) + string_decode_list("${success_callback}") + ans(success_callback) + assign(success = process_handle.on_success.add("${success_callback}")) + endif() + if(error_callback) + string_decode_list("${error_callback}") + ans(error_callback) + assign(success = process_handle.on_error.add("${error_callback}")) + endif() + if(process_callback) + message(yolo) + string_decode_list("${process_callback}") + ans(process_callback) + assign(success = process_handle.on_state_change.add("${process_callback}")) + endif() + + if(async) + process_start(${process_handle}) + return(${process_handle}) + else() + process_execute(${process_handle}) + if(return_handle) + return(${process_handle}) + endif() + + + + map_tryget(${process_handle} exit_code) + ans(exit_code) + + if(return_exit_code) + return_ref(exit_code) + endif() + + map_tryget(${process_handle} pid) + ans(pid) + if(NOT pid) + message(FATAL_ERROR FORMAT "could not find command '{start_info.command}'") + endif() + + if(exit_code AND silent_fail) + error("process {start_info.command} failed with {process_handle.exit_code}") + return() + endif() + + if(exit_code) + message(FATAL_ERROR FORMAT "process {start_info.command} failed with {process_handle.exit_code}") + endif() + + + map_tryget(${process_handle} stdout) + ans(stdout) + return_ref(stdout) + + endif() + + +endfunction() \ No newline at end of file diff --git a/cmake/process/linux/linux_ps_info.cmake b/cmake/process/linux/linux_ps_info.cmake index 93daf271..f557ac40 100644 --- a/cmake/process/linux/linux_ps_info.cmake +++ b/cmake/process/linux/linux_ps_info.cmake @@ -1,15 +1,15 @@ function(linux_ps_info pid key) - linux_ps(-p "${pid}" -o "${key}=" --result) + linux_ps(-p "${pid}" -o "${key}=" --process-handle) ans(res) - map_tryget(${res} return_code) - ans(return_code) - if(NOT "${return_code}" EQUAL 0) + map_tryget(${res} exit_code) + ans(erro) + if(NOT "${erro}" EQUAL 0) return() endif() - map_tryget(${res} output) + map_tryget(${res} stdout) ans(stdout) string(STRIP "${stdout}" val) diff --git a/cmake/process/linux/process_kill_Linux.cmake b/cmake/process/linux/process_kill_Linux.cmake index 85c53b77..38f0c5e6 100644 --- a/cmake/process/linux/process_kill_Linux.cmake +++ b/cmake/process/linux/process_kill_Linux.cmake @@ -8,11 +8,8 @@ map_tryget(${handle} pid) ans(pid) - linux_kill(-SIGTERM ${pid} --result) - ans(res) + linux_kill(-SIGTERM ${pid} --exit-code) + ans(error) - map_tryget(${res} return_code) - ans(return_code) - - return_truth("${return_code}" EQUAL 0) + return_truth("${error}" EQUAL 0) endfunction() \ No newline at end of file diff --git a/cmake/process/linux/process_start_Linux.cmake b/cmake/process/linux/process_start_Linux.cmake index 030dd1b3..1cc85fde 100644 --- a/cmake/process/linux/process_start_Linux.cmake +++ b/cmake/process/linux/process_start_Linux.cmake @@ -2,64 +2,71 @@ # process_fork implementation specific to linux # uses bash and nohup to start a process -function(process_start_Linux) - process_start_info(${ARGN}) - ans(process_start_info) +function(process_start_Linux process_handle) + process_handle_register(${process_handle}) + map_tryget(${process_handle} start_info) - if(NOT process_start_info) - return() - endif() + ans(process_start_info) - scope_import_map(${process_start_info}) + map_tryget(${process_start_info} command) + ans(command) - command_line_args_combine(${args}) - ans(arg_string) - set(command_string "${command} ${arg_string}") - + map_tryget(${process_start_info} command_arguments) + ans(command_arguments) + + + command_line_args_combine(${command_arguments}) + ans(command_arguments_string) + + set(command_string "${command} ${command_arguments_string}") + # define output files + file_make_temporary("") + ans(stdout) + file_make_temporary("") + ans(stderr) + file_make_temporary("") + ans(return_code) + file_make_temporary("") + ans(pid_out) + process_handle_change_state(${process_handle} starting) + # create a temporary shell script + # which starts bash with the specified command + # output of the command is stored in stdout file + # error of the command is stored in stderr file + # return_code is stored in return_code file + # and the created process id is stored in pid_out + shell_tmp_script("( bash -c \"${command_string} > ${stdout} 2> ${stderr}\" ; echo $? > ${return_code}) & echo $! > ${pid_out}") + ans(script) + ## execute the script in bash with nohup + ## which causes the script to run detached from process + bash(-c "nohup ${script} > /dev/null 2> /dev/null" --exit-code) + ans(error) - # define output files - file_make_temporary("") - ans(stdout) - file_make_temporary("") - ans(stderr) - file_make_temporary("") - ans(return_code) - file_make_temporary("") - ans(pid_out) + if(error) + message(FATAL_ERROR "could not start process '${command_string}'") + endif() - # create a temporary shell script - # which starts bash with the specified command - # output of the command is stored in stdout file - # error of the command is stored in stderr file - # return_code is stored in return_code file - # and the created process id is stored in pid_out - shell_tmp_script("( bash -c \"${command_string} > ${stdout} 2> ${stderr}\" ; echo $? > ${return_code}) & echo $! > ${pid_out}") - ans(script) - ## execute the script in bash with nohup - ## which causes the script to run detached from process - bash(-c "nohup ${script} > /dev/null 2> /dev/null" --return_code) - ans(error) - if(error) - message(FATAL_ERROR "could not start process '${command_string}'") - endif() - fread("${pid_out}") - ans(pid) + fread("${pid_out}") + ans(pid) - string(STRIP "${pid}" pid) + string(STRIP "${pid}" pid) - process_handle("${pid}") - ans(handle) + map_set(${process_handle} pid "${pid}") - ## set output of process - nav(handle.stdout_file = stdout) - nav(handle.stderr_file = stderr) - nav(handle.return_code_file = return_code) + process_handle_change_state(${process_handle} running) + + + ## set output of process + map_set(${process_handle} stdout_file ${stdout}) + map_set(${process_handle} stderr_file ${stderr}) + map_set(${process_handle} return_code_file ${return_code}) + - process_refresh_handle("${handle}") + process_refresh_handle("${process_handle}") - return_ref(handle) + return_ref(process_handle) endfunction() diff --git a/cmake/process/process_execute.cmake b/cmake/process/process_execute.cmake new file mode 100644 index 00000000..89de1546 --- /dev/null +++ b/cmake/process/process_execute.cmake @@ -0,0 +1,102 @@ +## `()->` +## +## executes the specified command with the specified arguments in the +## current working directory +## creates and registers a process handle which is then returned +## this function accepts arguments as encoded lists. this allows you to include +## arguments which contain semicolon and other special string chars. +## the process id of processes start with `process_execute` is always -1 +## because `CMake`'s `execute_process` does not return it. This is not too much of a problem +## because the process will always be terminated as soon as the function returns +## +## **parameters** +## * `` the executable (may contain spaces) +## * `` the arguments - may be an encoded list +## **scope** +## * `pwd()` used as the working-directory +## **events** +## * `on_process_handle_created` global event is emitted when the process_handle is ready +## * `process_handle.on_state_changed` +## +## **returns** +## ``` +## ::= { +## pid: "-1"|"0" +## +## } +## ``` +function(process_execute process_handle) + process_handle_register(${process_handle}) + + map_tryget(${process_handle} start_info) + ans(process_start_info) + + ## the pid is -1 by default for non async processes + map_set(${process_handle} pid -1) + + ## register process handle + process_handle_change_state(${process_handle} starting) + process_handle_change_state(${process_handle} running) + + map_tryget(${process_start_info} working_directory) + ans(cwd) + + map_tryget(${process_start_info} command) + ans(command) + + cmake_string_escape("${command}") + ans(command) + + map_tryget(${process_start_info} command_arguments) + ans(command_arguments) + + + #command_line_args_combine(${command_arguments}) + #ans(command_arguments_string) + + set(command_arguments_string) + foreach(argument ${command_arguments}) + string_decode_list("${argument}") + ans(argument) + cmake_string_escape("${argument}") + ans(argument) + set(command_arguments_string "${command_arguments_string} ${argument}") + endforeach() + + + map_tryget(${process_start_info} timeout) + ans(timeout) + + + if("${timeout}" GREATER -1) + set(timeout TIMEOUT ${timeout}) + else() + set(timeout) + endif() + + set(eval_this " + execute_process( + COMMAND ${command} ${command_arguments_string} + RESULT_VARIABLE exit_code + OUTPUT_VARIABLE stdout + ERROR_VARIABLE stderr + WORKING_DIRECTORY ${cwd} + ${timeout} + ) + ") +# _message("${eval_this}") + eval_ref(eval_this) + + ## set process handle variables + if(NOT "${exit_code}" MATCHES "^-?[0-9]+$") + map_set(${process_handle} pid) + endif() + map_set(${process_handle} exit_code "${exit_code}") + map_set(${process_handle} stdout "${stdout}") + map_set(${process_handle} stderr "${stderr}") + + ## change state + process_handle_change_state(${process_handle} terminated) + + return_ref(process_handle) +endfunction() diff --git a/cmake/process/process_handle_change_state.cmake b/cmake/process/process_handle_change_state.cmake new file mode 100644 index 00000000..0aeb12ac --- /dev/null +++ b/cmake/process/process_handle_change_state.cmake @@ -0,0 +1,34 @@ + +function(process_handle_change_state process_handle new_state) + map_tryget("${process_handle}" state) + ans(old_state) + if("${old_state}" STREQUAL "${new_state}") + return(false) + endif() + + map_tryget(${process_handle} on_state_change) + ans(on_state_change_event) + + event_emit(${on_state_change_event} ${process_handle}) + + + + map_set(${process_handle} state "${new_state}") + + if("${new_state}" STREQUAL "terminated") + map_tryget(${process_handle} exit_code) + ans(error) + if(error) + map_tryget("${process_handle}" on_error) + ans(on_error_event) + event_emit("${on_error_event}" ${process_handle}) + else() + map_tryget("${process_handle}" on_success) + ans(on_success_event) + event_emit("${on_success_event}" ${process_handle}) + endif() + +endif() + + return(true) +endfunction() \ No newline at end of file diff --git a/cmake/process/process_handle_get.cmake b/cmake/process/process_handle_get.cmake new file mode 100644 index 00000000..8a8a7ef2 --- /dev/null +++ b/cmake/process/process_handle_get.cmake @@ -0,0 +1,5 @@ + +function(process_handle_get pid) + map_tryget(__process_handles ${pid}) + return_ans() +endfunction() \ No newline at end of file diff --git a/cmake/process/process_handle_new.cmake b/cmake/process/process_handle_new.cmake new file mode 100644 index 00000000..df8b9139 --- /dev/null +++ b/cmake/process/process_handle_new.cmake @@ -0,0 +1,30 @@ +## `()->` +## +## returns a new process handle which has the following layout: +## ``` +## ::= { +## pid: +## start_info: +## state: "unknown"|"running"|"terminated" +## stdout: +## stderr: +## exit_code: | +## command: +## command_args: +## on_state_change: [old_state, new_state](${process_handle}) +## } +## ``` +function(process_handle_new start_info) + map_new() + ans(process_handle) + map_set(${process_handle} pid "") + map_set(${process_handle} start_info "${start_info}") + map_set(${process_handle} state "unknown") + map_set(${process_handle} stdout "") + map_set(${process_handle} stderr "") + map_set(${process_handle} exit_code) + assign(process_handle.on_state_change = event_new()) + assign(process_handle.on_success = event_new()) + assign(process_handle.on_error = event_new()) + return_ref(process_handle) +endfunction() diff --git a/cmake/process/process_handle_register.cmake b/cmake/process/process_handle_register.cmake new file mode 100644 index 00000000..b2f3764b --- /dev/null +++ b/cmake/process/process_handle_register.cmake @@ -0,0 +1,4 @@ + +function(process_handle_register process_handle) + event_emit(on_process_handle_created ${process_handle}) +endfunction() diff --git a/cmake/process/process_info.cmake b/cmake/process/process_info.cmake index 5faaf582..30db695e 100644 --- a/cmake/process/process_info.cmake +++ b/cmake/process/process_info.cmake @@ -3,5 +3,6 @@ function(process_info) wrap_platform_specific_function(process_info) process_info(${ARGN}) - return_ans() + ans(res) + return_ref(res) endfunction() diff --git a/cmake/process/process_refresh_handle.cmake b/cmake/process/process_refresh_handle.cmake index eab7bf3d..86ed87e6 100644 --- a/cmake/process/process_refresh_handle.cmake +++ b/cmake/process/process_refresh_handle.cmake @@ -6,40 +6,32 @@ function(process_refresh_handle handle) ans(handle) - set(args ${ARGN}) - process_isrunning("${handle}") ans(isrunning) - - - - if(isrunning) set(state running) else() set(state terminated) endif() - # get old state update new state - map_tryget(${handle} state) - ans(previous_state) - map_set(${handle} state "${state}") + process_handle_change_state("${handle}" "${state}") + ans(state_changed) - if(NOT "${state}_" STREQUAL "${previous_state}_") - #message(FORMAT "statechange ({handle.pid}) : {previous_state} -> {state} ") + if(state_changed) if("${state}" STREQUAL "terminated") process_return_code("${handle}") - ans(return_code) + ans(exit_code) process_stdout("${handle}") ans(stdout) process_stderr("${handle}") ans(stderr) - map_capture("${handle}" return_code stdout stderr) + map_capture("${handle}" exit_code stdout stderr) endif() endif() - return(${isrunning}) + + return_ref(isrunning) endfunction() diff --git a/cmake/process/process_start_info_new.cmake b/cmake/process/process_start_info_new.cmake new file mode 100644 index 00000000..f96a43ee --- /dev/null +++ b/cmake/process/process_start_info_new.cmake @@ -0,0 +1,52 @@ +## `(|)->` +## ` ::= "COMMAND"? ` +## +## creates a new process_start_info with the following fields +## ``` +## ::= { +## command: +## command_arguments: +## working_directory: +## timeout: +## } +## ``` +## +## *example* +## * `process_start_info_new(COMMAND cmake -E echo "asd bsd" csd) -> <% process_start_info_new(COMMAND cmake -E echo "asd;bsd") +## ans(info) +## template_out_json(${info}) %>` +function(process_start_info_new) + arguments_encoded_list(${ARGC}) + ans(arguments_list) + + list_extract_any_labelled_value(arguments_list WORKING_DIRECTORY) + ans(working_directory) + list_extract_any_labelled_value(arguments_list TIMEOUT) + ans(timeout) + + if(NOT timeout) + set(timeout -1) + endif() + + path_qualify(working_directory) + + list_pop_front(arguments_list) + ans(command) + + if("${command}_" STREQUAL "COMMAND_") + list_pop_front(arguments_list) + ans(command) + endif() + + string_decode_list("${command}") + ans(command) + + map_new() + ans(process_start_info) + map_set(${process_start_info} command "${command}") + map_set(${process_start_info} command_arguments "${arguments_list}") + map_set(${process_start_info} working_directory "${working_directory}") + map_set(${process_start_info} timeout "${timeout}") + + return_ref(process_start_info) +endfunction() diff --git a/cmake/process/process_start_script.cmake b/cmake/process/process_start_script.cmake index c138169c..2b77bc9f 100644 --- a/cmake/process/process_start_script.cmake +++ b/cmake/process/process_start_script.cmake @@ -3,12 +3,13 @@ function(process_start_script scriptish) file_temp_name("{{id}}.cmake") ans(ppath) fwrite("${ppath}" "${scriptish}") - process_start( + execute( COMMAND "${CMAKE_COMMAND}" -P "${ppath}" ${ARGN} + --async ) return_ans() endfunction() \ No newline at end of file diff --git a/cmake/process/process_timeout.cmake b/cmake/process/process_timeout.cmake index 756cfc5d..807a126a 100644 --- a/cmake/process/process_timeout.cmake +++ b/cmake/process/process_timeout.cmake @@ -2,11 +2,11 @@ #todo create shims function(process_timeout n) if(${CMAKE_MAJOR_VERSION} GREATER 2) - process_start("{command:$CMAKE_COMMAND, args:['-E', 'sleep', $n]}") + execute(${CMAKE_COMMAND} -E sleep ${n} --async) return_ans() else() if(UNIX) - process_start("{command:'sleep', args:$n}") + execute(sleep ${n} --async) return_ans() endif() endif() diff --git a/cmake/process/process_wait_all.cmake b/cmake/process/process_wait_all.cmake index f0aa8207..1cfc47a6 100644 --- a/cmake/process/process_wait_all.cmake +++ b/cmake/process/process_wait_all.cmake @@ -1,67 +1,83 @@ - ## process_wait_all(> >) - ## waits for all specified to finish - ## specify --quietly to supress output - ## if --timeout is specified the function will return all finished processes after n seconds - function(process_wait_all) - set(args ${ARGN}) +## `(> [--timeout ] [--idle-callback ] [--task-complete-callback ] )` +## +## waits for all specified to finish returns them in the order +## in which they completed +## +## `--timeout ` if value is specified the function will return all +## finished processes after n seconds +## +## `--idle-callback ` +## if value is specified it will be called at least once +## and between every query if a task is still running +## +## `--task-complete-callback ` +## if value is specified it will be called whenever a +## task completes. +## +## *Example* +## `process_wait_all(${handle1} ${handle1} --task-complete-callback "[](handle)message(FORMAT '{handle.pid}')")` +## prints the process id to the console whenver a process finishes +## +function(process_wait_all) + set(args ${ARGN}) - list_extract_flag(args --quietly) - ans(quietly) + list_extract_labelled_value(args --idle-callback) + ans(idle_callback) - list_extract_labelled_value(args --timeout) - ans(timeout) - set(timeout_task_handle) - + list_extract_labelled_value(args --task-complete-callback) + ans(task_complete_callback) - process_handles(${args}) - ans(processes) + list_extract_labelled_value(args --timeout) + ans(timeout) + set(timeout_task_handle) - list(LENGTH processes running_processes) - set(process_count ${running_processes}) - if(NOT quietly) - echo_append("waiting for ${running_processes} processes to complete.") - endif() + process_handles(${args}) + ans(processes) + + + list(LENGTH processes running_processes) + set(process_count ${running_processes}) + + set(timeout_process_handle) + if(timeout) + process_timeout(${timeout}) + ans(timeout_process_handle) + list(APPEND processes ${timeout_process_handle}) + endif() - set(timeout_process_handle) - if(timeout) - process_timeout(${timeout}) - ans(timeout_process_handle) - list(APPEND processes ${timeout_process_handle}) + while(processes) + + if(idle_callback) + call2("${idle_callback}") endif() - while(processes) - list_pop_front(processes) - ans(process) - process_refresh_handle(${process}) - ans(isrunning) - - #message(FORMAT "{process.pid} isrunning {isrunning} {process.state} ") - if(NOT quietly) - tick() - endif() + list_pop_front(processes) + ans(process) + process_refresh_handle(${process}) + ans(isrunning) + + #message(FORMAT "{process.pid} isrunning {isrunning} {process.state} ") + + if(NOT isrunning) + if("${process}_" STREQUAL "_${timeout_process_handle}") + set(processes) + else() - if(NOT isrunning) - if("${process}_" STREQUAL "_${timeout_process_handle}") - set(processes) - if(NOT quietly) - echo_append(".. timeout") + list(APPEND finished ${process}) + if(NOT quietly) + list(LENGTH finished finished_count) + if(task_complete_callback) + call2("${task_complete_callback}" "${process}") endif() - else() - list(APPEND finished ${process}) - if(NOT quietly) - list(LENGTH finished finished_count) - echo_append("..${finished_count}/${process_count}") - endif() - endif() - else() - ## insert into back - list(APPEND processes ${process}) - endif() - endwhile() - if(NOT quietly) - echo() + endif() + endif() + else() + ## insert into back + list(APPEND processes ${process}) endif() - return_ref(finished) - endfunction() + endwhile() + + return_ref(finished) +endfunction() diff --git a/cmake/process/process_wait_any.cmake b/cmake/process/process_wait_any.cmake index c9c23fda..8b936f8a 100644 --- a/cmake/process/process_wait_any.cmake +++ b/cmake/process/process_wait_any.cmake @@ -3,6 +3,7 @@ ## if --timeout is specified function will return nothing after n seconds function(process_wait_any) set(args ${ARGN}) + list_extract_flag(args --quietly) ans(quietly) diff --git a/cmake/process/real_world_parallel_process_example.cmake b/cmake/process/real_world_parallel_process_example.cmake deleted file mode 100644 index abbab7e4..00000000 --- a/cmake/process/real_world_parallel_process_example.cmake +++ /dev/null @@ -1,65 +0,0 @@ -function(test) - # real world example - - - ## define a function which downloads - ## all urls specified to the current dir - ## returns the path for every downloaded files - function(download_files_parallel) - ## get current working dir - pwd() - ans(target_dir) - - ## process start loop - ## starts a new process for every url to download - set(handles) - foreach(url ${ARGN}) - ## start download by creating a cmake script - process_start_script(" - include(${oocmake_base_dir}/cmakepp.cmake) # include oocmake - download(\"${url}\" \"${target_dir}\") - ans(result_path) - message(STATUS ${target_dir}/\${result_path}) - ") - ans(handle) - ## store process handle - list(APPEND handles ${handle}) - endforeach() - - ## wait for all downloads to finish - process_wait_all(${handles}) - - set(result_paths) - foreach(handle ${handles}) - ## get process stdout - process_stdout(${handle}) - ans(output) - - ## remove '-- ' from beginning of output which is - ## automatically prependend by message(STATUS) - string(SUBSTRING "${output}" 3 -1 output) - - ## store returned file path - list(APPEND result_paths ${output}) - - endforeach() - - ## return file paths of downloaded files - return_ref(result_paths) - endfunction() - - - ## create and goto ./download_dir - cd("download_dir" --create) - - ## start downloading files in parallel - download_files_parallel( - http://www.cmake.org/files/v3.0/cmake-3.0.2.tar.gz - http://www.cmake.org/files/v2.8/cmake-2.8.12.2.tar.gz - ) - ans(paths) - - - assert(paths) - -endfunction() \ No newline at end of file diff --git a/cmake/process/windows/process_info_Windows.cmake b/cmake/process/windows/process_info_Windows.cmake index dc4161fb..f221d2e9 100644 --- a/cmake/process/windows/process_info_Windows.cmake +++ b/cmake/process/windows/process_info_Windows.cmake @@ -7,24 +7,25 @@ function(process_info_Windows handlish) ans(pid) - win32_tasklist(/V /FO CSV /FI "PID eq ${pid}" --result ) + win32_tasklist(/V /FO CSV /FI "PID eq ${pid}" --process-handle ) ans(exe_result) - map_tryget(${exe_result} return_code) + + map_tryget(${exe_result} exit_code) ans(error) if(error) return() endif() - map_tryget(${exe_result} output) + map_tryget(${exe_result} stdout) ans(csv) + csv_deserialize("${csv}" --headers) ans(res) map_rename(${res} PID pid) - return_ref(res) endfunction() diff --git a/cmake/process/windows/process_isrunning_Windows.cmake b/cmake/process/windows/process_isrunning_Windows.cmake index 1581464d..9cc3b6ed 100644 --- a/cmake/process/windows/process_isrunning_Windows.cmake +++ b/cmake/process/windows/process_isrunning_Windows.cmake @@ -18,4 +18,5 @@ function(process_isrunning_Windows handlish) return(true) endif() return(false) -endfunction() \ No newline at end of file +endfunction() + diff --git a/cmake/process/windows/process_kill_Windows.cmake b/cmake/process/windows/process_kill_Windows.cmake index 4bf91364..6d714317 100644 --- a/cmake/process/windows/process_kill_Windows.cmake +++ b/cmake/process/windows/process_kill_Windows.cmake @@ -5,10 +5,9 @@ function(process_kill_Windows process_handle) map_tryget(${process_handle} pid) ans(pid) - win32_taskkill(/PID ${pid} --result) - ans(res) - scope_import_map(${res}) - if(error_code) + win32_taskkill(/PID ${pid} --exit-code) + ans(exit_code) + if(exit_code) return(false) endif() return(true) diff --git a/cmake/process/windows/process_list_Windows.cmake b/cmake/process/windows/process_list_Windows.cmake index 5845c46c..645e0851 100644 --- a/cmake/process/windows/process_list_Windows.cmake +++ b/cmake/process/windows/process_list_Windows.cmake @@ -6,13 +6,12 @@ function(process_list_Windows) string(REGEX MATCHALL "[0-9]+" matches "${ids}") set(ids) + foreach(id ${matches}) process_handle("${id}") ans(handle) - list(APPEND ids ${id}) + list(APPEND ids ${handle}) endforeach() return_ref(ids) - - endfunction() \ No newline at end of file diff --git a/cmake/process/windows/process_start_Windows.cmake b/cmake/process/windows/process_start_Windows.cmake index 50e64e5b..d1f36d91 100644 --- a/cmake/process/windows/process_start_Windows.cmake +++ b/cmake/process/windows/process_start_Windows.cmake @@ -1,76 +1,58 @@ ## windows implementation for start process - function(process_start_Windows) - process_start_info(${ARGN}) +## newer faster version + function(process_start_Windows process_handle) + ## create a process handle from pid + process_handle_register(${process_handle}) + + map_tryget(${process_handle} start_info) ans(start_info) - if(NOT start_info) - message(WARNING " could not be parsed from '${ARGN}'") - return() - endif() map_tryget(${start_info} command) ans(command) - map_tryget(${start_info} args) - ans(args) + map_tryget(${start_info} command_arguments) + ans(command_arguments) + + command_line_args_combine(${command_arguments}) + ans(command_arguments_string) + + set(command_string "${command} ${command_arguments_string}") - map_tryget(${start_info} cwd) - ans(cwd) + map_tryget(${start_info} working_directory) + ans(working_directory) ## create temp dir where process specific files are stored mktemp() ans(dir) - ## files where to store stdout and stderr set(outputfile "${dir}/stdout.txt") set(errorfile "${dir}/stderr.txt") set(returncodefile "${dir}/retcode.txt") set(pidfile "${dir}/pid.txt") + fwrite("${outputfile}" "") fwrite("${errorfile}" "") fwrite("${returncodefile}" "") - ## compile arglist - win32_powershell_create_array(${args}) - ans(arg_list) - - ## The following script is a bit complex but it has to be to get the commands - ## pid,stdout, stderr and return_code correctly - ## two instances of powershell need to be started for this to work correctly - - ## innerscript which starts the process in ${cwd} using powershell's start-process command - ## with redirected error and output streams - ## it immediately writes the process id to ${pidfile} - ## then it waits for process to finish - ## after which it parses the exit code of said process - set(inner " - $j = start-process -filepath '${command}' -argumentlist ${arg_list} -passthru -redirectstandardout '${outputfile}' -redirectstandarderror '${errorfile}' -workingdirectory '${cwd}' - $handle = $j.handle - echo $j.id | out-file -encoding ascii -filepath '${pidfile}' - wait-process -id $j.id - $code = '[DllImport(\"kernel32.dll\")] public static extern int GetExitCodeProcess(IntPtr hProcess, out Int32 exitcode);' - $type = Add-Type -MemberDefinition $code -Name \"Win32\" -Namespace Win32 -PassThru - [Int32]$exitCode = 0 - $type::GetExitCodeProcess($j.Handle, [ref]$exitCode) - echo $exitCode | out-file -encoding ascii -filepath '${returncodefile}' - ") - ## store innerscript so it can be called by outer script - fwrite("${dir}/inner.ps1" "${inner}") - ans(inner_script) - - ## starts a new powershell process executing the innerscript and exits - ## this wrapping is needed because redirectstandarderror and redirectstandardout - ## will cause powershell to wait for the stream to end which happens when the process is finished. - ## hides the window - ## on execution you will notice that two windows open in quick succession - set(outer " - start-process -WindowStyle Hidden -filepath powershell -argumentlist @('-NoLogo','-NonInteractive','-ExecutionPolicy','ByPass','-WindowStyle','Hidden','-File','${inner_script}') - exit - ") - - ## run script - win32_powershell_run_script("${outer}") - ans(pid) + + ## creates a temporary batch file + ## which gets the process id (get the parent process id wmic....) + ## output pid to file output command_string to + file_tmp("bat" " + @echo off + cd ${working_directory} + wmic process get parentprocessid,name|find \"WMIC\" > ${pidfile} + ${command_string} > ${outputfile} 2> ${errorfile} + echo %errorlevel% > ${returncodefile} + exit + ") + ans(path) + + + process_handle_change_state(${process_handle} starting) + win32_powershell("start-process -File ${path} -WindowStyle Hidden") + ## wait until the pidfile exists and contains a valid pid ## this seems very hackisch but is necessary as i have not found @@ -80,52 +62,22 @@ fread("${pidfile}") ans(pid) if("${pid}" MATCHES "[0-9]+" ) + set(pid "${CMAKE_MATCH_0}") break() endif() endif() endwhile() - - - ## create a process handle from pid - process_handle("${pid}") - ans(handle) + map_set(${process_handle} pid "${pid}") + + process_handle_change_state(${process_handle} running) - ## set the output files for handle - nav(handle.stdout_file = outputfile) - nav(handle.stderr_file = errorfile) - nav(handle.return_code_file = returncodefile) - nav(handle.process_start_info = start_info) - nav(handle.windows.process_data_dir = dir) + ## set the output files for process_handle + map_set(${process_handle} stdout_file ${outputfile}) + map_set(${process_handle} stderr_file ${errorfile}) + map_set(${process_handle} return_code_file ${returncodefile}) + assign(!process_handle.windows.process_data_dir = dir) - return_ref(handle) + return_ref(process_handle) endfunction() - - -## old implementation with wmic -## does nto handle output and -# function(process_start_Windows) -# process_start_info(${ARGN}) -# ans(process_start_info) - -# if(NOT process_start_info) -# return() -# endif() - -# command_line_to_string(${process_start_info}) -# ans(command_line) - -# win32_fork(-exec "${command_line}" -workdir "${cwd}" --result) -# ans(exec_result) -# scope_import_map(${exec_result}) -# if(return_code) -# json_print(${exec_result}) -# message(FATAL_ERROR "failed to fork process. returned code was ${return_code} message:\n ${stdout} ") -# endif() - -# string(REGEX MATCH "[1-9][0-9]*" pid "${stdout}") -# set(status running) -# map_capture_new(pid process_start_info status) -# return_ans() -# endfunction() diff --git a/cmake/process/windows/win32_fork.cmake b/cmake/process/windows/win32_fork.cmake deleted file mode 100644 index 89b885ad..00000000 --- a/cmake/process/windows/win32_fork.cmake +++ /dev/null @@ -1,8 +0,0 @@ -#wraps the windows 32 for script which starts a new executable in a separate process and returns the PID -function(win32_fork) - oocmake_config(base_dir) - ans(base_dir) - wrap_executable(win32_fork "${base_dir}/resources/exec_windows.bat") - win32_fork(${ARGN}) - return_ans() -endfunction() \ No newline at end of file diff --git a/cmake/process/windows/win32_powershell_run_script.cmake b/cmake/process/windows/win32_powershell_run_script.cmake index e5a3bcf4..5e592d28 100644 --- a/cmake/process/windows/win32_powershell_run_script.cmake +++ b/cmake/process/windows/win32_powershell_run_script.cmake @@ -11,7 +11,8 @@ -NoLogo # no info output -NonInteractive # no interaction -ExecutionPolicy ByPass # bypass execution policy - -WindowStyle Hidden # hide window + # -NoNewWindow + #-WindowStyle Hidden # hide window -File "${script_file}" # the file to execute ${ARGN} # add further args to command line ) diff --git a/cmake/process/windows/win32_tasklist.cmake b/cmake/process/windows/win32_tasklist.cmake index aaabc528..502f3bcc 100644 --- a/cmake/process/windows/win32_tasklist.cmake +++ b/cmake/process/windows/win32_tasklist.cmake @@ -3,4 +3,5 @@ function(win32_tasklist) wrap_executable(win32_tasklist "tasklist") win32_tasklist(${ARGN}) return_ans() -endfunction() \ No newline at end of file +endfunction() + diff --git a/cmake/process/wrap_executable.cmake b/cmake/process/wrap_executable.cmake index 7c3e8dd9..e52eae5e 100644 --- a/cmake/process/wrap_executable.cmake +++ b/cmake/process/wrap_executable.cmake @@ -8,14 +8,14 @@ # Warning: --async is a bit experimental # # defines function -# ([--async]|[--result]|[--return-code])-> ||| +# ([--async]|[--process-handle]|[--exit-code])-> ||| # # if no flag is specified then the function will fail if the return code is not 0 # if it succeeds the return value is the stdout # -# --result flag the function will return a the execution +# --process-handle flag the function will return a the execution # result object (see execute()) -# --return-code flag the function will return the returncode +# --exit-code flag the function will return the exit code # --async will execute the executable asynchroniously and # return a # --async-wait will execute the executable asynchroniously @@ -25,86 +25,18 @@ # else only the application output will be returned # and if the application terminates with an exit code != 0 a fatal error will be raised function(wrap_executable alias executable) - set_ans("") - - set(predefined_arg_string) - foreach(arg ${ARGN}) - set(predefined_arg_string "${predefined_arg_string} \"${arg}\"") - endforeach() + arguments_encoded_list(${ARGC}) + ans(arguments) + # remove alias and executable + list_pop_front(arguments) + list_pop_front(arguments) eval(" function(${alias}) - pwd() - ans(cwd) - if(NOT IS_DIRECTORY \"\${cwd}\") - message(FATAL_ERROR \"${alias}: '\${cwd}' is not a directory, try setting it via cd()\") - endif() - set(cmd_line_args ${predefined_arg_string} \${ARGN}) - list_extract_flag(cmd_line_args --result) - ans(result_flag) - list_extract_flag(cmd_line_args --return-code) - ans(return_code_flag) - list_extract_flag(cmd_line_args --async) - ans(async) - list_extract_flag(cmd_line_args --async-wait) - ans(wait) - - set(executable \"${executable}\") - - if(async OR wait) - process_start(\"{ - path:$executable, - args:$cmd_line_args, - cwd:$cwd - }\") - ans(process_handle) - if(NOT wait) - return_ref(process_handle) - endif() - - get_filename_component(name \${executable} NAME_WE) - - echo_append(\"waiting for \${name} process to finish ...\") - set(is_running true) - while(is_running) - tick() - process_refresh_handle(\${process_handle}) - ans(is_running) - endwhile() - message(\"... done\") - else() - execute(\"{ - path:$executable, - args:$cmd_line_args, - cwd:$cwd - }\") - ans(process_handle) - endif() - - if(result_flag) - return(\${process_handle}) - endif() - - map_tryget(\${process_handle} return_code) - ans(error) - if(NOT error) - set(error 0) - endif() - - if(return_code_flag) - return_ref(error) - endif() - map_tryget(\${process_handle} stdout) - ans(stdout) - - if(NOT \"\${error}\" EQUAL 0) - - message(FATAL_ERROR \"failed to execute ${alias} - return code is '\${error}'\\n stderr:\\n \${stdout} \") - - return() - endif() - - return_ref(stdout) + arguments_encoded_list(\${ARGC}) + ans(arguments) + execute(\"${executable}\" ${arguments} \${arguments}) + return_ans() endfunction() ") return() diff --git a/cmake/quickmap/key.cmake b/cmake/quickmap/key.cmake index fcd372ab..10f3e117 100644 --- a/cmake/quickmap/key.cmake +++ b/cmake/quickmap/key.cmake @@ -23,7 +23,8 @@ function(key key) string_take_address(current_key) ans(current_ref) - map_isvalid("${current_ref}") + #map_isvalid("${current_ref}") + ref_isvalid("${current_ref}") ans(ismap) if(NOT ismap) message(FATAL_ERROR "expected a map before key() call") diff --git a/cmake/ref/ref_isvalid.cmake b/cmake/ref/ref_isvalid.cmake index 65f2d694..30b741d6 100644 --- a/cmake/ref/ref_isvalid.cmake +++ b/cmake/ref/ref_isvalid.cmake @@ -8,4 +8,27 @@ function(ref_isvalid ref) return(true) endif() return(false) +endfunction() + +## faster - does not work in all cases +macro(ref_isvalid ref) + if("_${ref}" MATCHES "^_:[^;]+$") + set(__ans true) + else() + set(__ans false) + endif() +endmacro() + + +## correcter +## the version above cannot be used because +## ref_isvalid gets arbirtray data - and since macros evaluate +## arguments a invalid ref could be ssen as valid +## or especially \\ fails because it beomes \ and causes an error +function(ref_isvalid ref) + if("_${ref}" MATCHES "^_:[^;]+$") + set(__ans true PARENT_SCOPE) + else() + set(__ans false PARENT_SCOPE) + endif() endfunction() \ No newline at end of file diff --git a/cmake/reg/reg_delete_value.cmake b/cmake/reg/reg_delete_value.cmake index ef8e96c6..e14e1496 100644 --- a/cmake/reg/reg_delete_value.cmake +++ b/cmake/reg/reg_delete_value.cmake @@ -3,7 +3,7 @@ ## removes the specified value from the windows registry function(reg_delete_value key valueName) string(REPLACE / \\ key "${key}") - reg(delete "${key}" /v "${valueName}" /f --return-code) + reg(delete "${key}" /v "${valueName}" /f --exit-code) ans(error) if(error) return(false) diff --git a/cmake/reg/reg_query.cmake b/cmake/reg/reg_query.cmake index 5c6b7d87..bdf34243 100644 --- a/cmake/reg/reg_query.cmake +++ b/cmake/reg/reg_query.cmake @@ -4,14 +4,14 @@ ## returns a list of entries containing all direct child elements function(reg_query key) string(REPLACE / \\ key "${key}") - reg(query "${key}" --result) + reg(query "${key}" --process-handle) ans(res) - map_tryget(${res} output) + map_tryget(${res} stdout) ans(output) - map_tryget(${res} result) + map_tryget(${res} exit_code) ans(error) if(error) diff --git a/cmake/reg/reg_write_value.cmake b/cmake/reg/reg_write_value.cmake index eb4cfef2..19e92a57 100644 --- a/cmake/reg/reg_write_value.cmake +++ b/cmake/reg/reg_write_value.cmake @@ -6,7 +6,7 @@ ans(value) string(REPLACE / \\ key "${key}") set(type REG_SZ) - reg(add "${key}" /v "${value_name}" /t "${type}" /f /d "${value}" --return-code) + reg(add "${key}" /v "${value_name}" /t "${type}" /f /d "${value}" --exit-code) ans(error) if(error) return(false) diff --git a/cmake/regex/regex_all.cmake b/cmake/regex/regex_all.cmake new file mode 100644 index 00000000..09a177f3 --- /dev/null +++ b/cmake/regex/regex_all.cmake @@ -0,0 +1,5 @@ + + function(regex_all regex) + string(REGEX MATCHALL "${regex}" ans ${ARGN}) + return_ref(ans) + endfunction() \ No newline at end of file diff --git a/cmake/regex/regex_cmake.cmake b/cmake/regex/regex_cmake.cmake new file mode 100644 index 00000000..a1fa7853 --- /dev/null +++ b/cmake/regex/regex_cmake.cmake @@ -0,0 +1,51 @@ +macro(regex_cmake) +#http://www.cmake.org/cmake/help/v3.0/manual/cmake-language.7.html#grammar-token-regex_cmake_escape_sequence + set(regex_cmake_newline "\n") + set(regex_cmake_space_chars " \t") + set(regex_cmake_space "[${regex_cmake_space_chars}]+") + set(regex_cmake_line_comment "#([^${regex_cmake_newline}]*)") + set(regex_cmake_line_comment.comment CMAKE_MATCH_1) + + set(regex_cmake_line_ending "(${regex_cmake_line_comment})?(${regex_cmake_newline})") + + set(regex_cmake_separation "(${regex_cmake_space})|(${regex_cmake_line_ending})") + + set(regex_cmake_identifier "[A-Za-z_][A-Za-z0-9_]*") + + set(regex_cmake_bracket_open "[(=+)[") + set(regex_cmake_bracket_close "](=+)]") + set(regex_cmake_bracket_content ".*") + set(regex_cmake_bracket_argument "${regex_cmake_bracket_open}${regex_cmake_bracket_content}${regex_cmake_bracket_close}") + + + set(regex_cmake_backslash "\\\\") + + #set(escape_identity "(\\\\()|\\\\)|\\\\#|\\\\\"|\\\\ |) + #set(escape_encoded "\\\\t|\\\\r|\\\\n") + set(regex_cmake_escape_semicolon "\\\;") + set(regex_cmake_escape_sequence "(${escape_identity})|(${escape_encoded})|(${regex_cmake_escape_semicolon})") + set(regex_cmake_quoted_continuation "\\\\\n") + set(regex_cmake_quoted_element "([^\"${regex_cmake_backslash}])|(${regex_cmake_escape_sequence})|(${regex_cmake_quoted_continuation})") + set(regex_cmake_quoted_argument "\"()\"") + + set(regex_cmake_argument "${regex_cmake_bracket_argument}|${regex_cmake_quoted_argument}|${unquoted_argument}") + set(regex_cmake_separated_arguments "((${regex_cmake_separation})+(${regex_cmake_argument})?)|") + set(regex_cmake_arguments "(${regex_cmake_argument})?(${regex_cmake_separated_arguments})*") + + set(regex_cmake_argument_string ".*") + set(regex_cmake_command_invocation "(${regex_cmake_space})*(${regex_cmake_identifier})(${regex_cmake_space})*\\((${regex_cmake_argument_string})\\)") + set(regex_cmake_command_invocation.regex_cmake_identifier CMAKE_MATCH_2) + set(regex_cmake_command_invocation.arguments CMAKE_MATCH_4) + + set(regex_cmake_separated_arguments) + set(regex_cmake_file_element "(${regex_cmake_command_invocation}${regex_cmake_line_ending})|((${bracket_comment}|${regex_cmake_space})*|(${regex_cmake_line_ending}))") + set(regex_cmake_file "(${regex_cmake_file_element})*") + + + set(regex_cmake_function_begin "(^|${cmake_regex_newline})(${regex_cmake_space})?function(${regex_cmake_space})?\\([^\\)\\(]*\\)") + set(regex_cmake_function_end "(^|${cmake_regex_newline})(${regex_cmake_space})?endfunction(${regex_cmake_space})?\\(([^\\)\\(]*)\\)") + set(regex_cmake_function_signature "(^|${cmake_regex_newline})((${regex_cmake_space})?)(${regex_cmake_identifier})((${regex_cmake_space})?)\\([${regex_cmake_space_chars}${regex_cmake_newline}]*(${regex_cmake_identifier})(.*)\\)") + set(regex_cmake_function_signature.name CMAKE_MATCH_7) + set(regex_cmake_function_signature.args CMAKE_MATCH_8) + +endmacro() \ No newline at end of file diff --git a/cmake/regex/regex_match.cmake b/cmake/regex/regex_match.cmake new file mode 100644 index 00000000..9f4353ce --- /dev/null +++ b/cmake/regex/regex_match.cmake @@ -0,0 +1,5 @@ + + function(regex_match regex) + string(REGEX MATCH "${regex}" ans ${ARGN}) + return_ref(ans) + endfunction() \ No newline at end of file diff --git a/cmake/regex/regex_replace.cmake b/cmake/regex/regex_replace.cmake new file mode 100644 index 00000000..4baf5543 --- /dev/null +++ b/cmake/regex/regex_replace.cmake @@ -0,0 +1,5 @@ + + function(regex_replace regex replace) + string(REGEX REPLACE "${regex}" "${replace}" ans ${ARGN}) + return_ref(ans) + endfunction() \ No newline at end of file diff --git a/cmake/sample/sample_copy.cmake b/cmake/sample/sample_copy.cmake index c259e0ba..ffdc958a 100644 --- a/cmake/sample/sample_copy.cmake +++ b/cmake/sample/sample_copy.cmake @@ -8,7 +8,7 @@ ans(target_dir) ## copy sample to test dir ## and compile cmakepp to test dir - oocmake_config(base_dir) + cmakepp_config(base_dir) ans(base_dir) glob("${base_dir}/samples/${sample}*") diff --git a/cmake/shell/alias_create.cmake b/cmake/shell/alias_create.cmake index 8ab4c612..09625357 100644 --- a/cmake/shell/alias_create.cmake +++ b/cmake/shell/alias_create.cmake @@ -4,10 +4,10 @@ function(alias_create name command_string) if(WIN32) - oocmake_config(bin_dir) + cmakepp_config(bin_dir) ans(bin_dir) set(path "${bin_dir}/${name}.bat") - file_write("${path}" "@echo off\r\n${command_string} %*") + fwrite("${path}" "@echo off\r\n${command_string} %*") reg_append_if_not_exists(HKCU/Environment Path "${bin_dir}") ans(res) if(res) @@ -29,7 +29,7 @@ function(alias_create name command_string) #message(INFO "alias ${name} was created - it will be available as soon as you restart your shell") else() - message(FATAL_ERROR "creating alias is not supported by oocmake on your system your current shell (${shell})") + message(FATAL_ERROR "creating alias is not supported by cmakepp on your system your current shell (${shell})") endif() endfunction() diff --git a/cmake/shell/alias_list.cmake b/cmake/shell/alias_list.cmake index 850038a6..f3afa28c 100644 --- a/cmake/shell/alias_list.cmake +++ b/cmake/shell/alias_list.cmake @@ -1,15 +1,20 @@ function(alias_list) + message(FATAL_ERROR "not implemented") path("${CMAKE_CURRENT_LIST_DIR}/../bin") ans(path) + + if(WIN32) - file_extended_glob("${path}" "*.bat" "!cps.*" "!cutil.*") - ans(cmds) + #file_extended_glob("${path}" "*.bat" "!cps.*" "!cutil.*") + ans(cmds) set(theRegex "([^\\/])+\\.bat") - list_select(cmds "(it)-> regex_search($it $theRegex 1)") + + list_select(cmds "[](it)regex_search({{it}} {{theRegex}})") ans(cmds) + string(REPLACE ".bat" "" cmds "${cmds}") return_ref(cmds) diff --git a/cmake/shell/bash_functions.cmake b/cmake/shell/bash_functions.cmake index 94fef2c2..2387f6ff 100644 --- a/cmake/shell/bash_functions.cmake +++ b/cmake/shell/bash_functions.cmake @@ -46,7 +46,7 @@ function(bash_autostart_register) fread("${session_profile_path}") ans(profile) - set(profile_path "$ENV{HOME}/oocmake.profile.sh") + set(profile_path "$ENV{HOME}/cmakepp.profile.sh") if(NOT EXISTS "${profile_path}") shell_script_create("${profile_path}" "") @@ -80,7 +80,7 @@ function(bash_autostart_unregister) endfunction() -# returs true if the oocmake session profile (environment variables)are registered +# returs true if the cmakepp session profile (environment variables)are registered function(bash_autostart_isregistered) set(session_profile_path "$ENV{HOME}/.profile") if(NOT EXISTS "${session_profile_path}") diff --git a/cmake/shell/shell.cmake b/cmake/shell/shell.cmake index cb799a75..ddf1dc18 100644 --- a/cmake/shell/shell.cmake +++ b/cmake/shell/shell.cmake @@ -18,29 +18,26 @@ function(shell cmd) # execute shell script which write the keyboard input to the ${value_file} set(args ${ARGN}) - list_extract_flag(args --result) - ans(result_flag) + list_extract_flag(args --process-handle) + ans(return_process_handle) - execute("{ - path:$shell_script, - args:$args - }") + execute("${shell_script}" ${args} --process-handle) ans(res) # remove temp file file(REMOVE "${shell_script}") - if(result_flag) + if(return_process_handle) return_ref(res) endif() - map_tryget(${res} result) - ans(return_code) + map_tryget(${res} exit_code) + ans(exit_code) - if(NOT "_${return_code}" STREQUAL "_0") + if(NOT "_${exit_code}" STREQUAL "_0") return() endif() - map_tryget(${res} output) - ans(output) - return_ref(output) + map_tryget(${res} stdout) + ans(stdout) + return_ref(stdout) endfunction() \ No newline at end of file diff --git a/cmake/string/README.md b/cmake/string/README.md index 10bb49d7..82f24a71 100644 --- a/cmake/string/README.md +++ b/cmake/string/README.md @@ -15,24 +15,22 @@ So I have created somewhat alot of functions which does things that you might ne * [cmake_string_escape](#cmake_string_escape) * [cmake_string_unescape](#cmake_string_unescape) * [delimiters](#delimiters) +* [argument_escape](#argument_escape) * [format](#format) * [regex_search](#regex_search) * [string_append_line_indented](#string_append_line_indented) * [string_char_at](#string_char_at) +* [string_codes](#string_codes) * [string_combine](#string_combine) +* [string_concat](#string_concat) * [string_contains](#string_contains) -* [string_decode_bracket](#string_decode_bracket) * [string_decode_delimited](#string_decode_delimited) -* [string_decode_empty](#string_decode_empty) -* [string_decode_list](#string_decode_list) -* [string_encode_bracket](#string_encode_bracket) -* [string_encode_delimited](#string_encode_delimited) -* [string_encode_empty](#string_encode_empty) -* [string_encode_list](#string_encode_list) * [string_ends_with](#string_ends_with) * [string_eval](#string_eval) +* [string_find](#string_find) * [string_isempty](#string_isempty) * [string_isnumeric](#string_isnumeric) +* [string_length](#string_length) * [string_lines](#string_lines) * [string_match](#string_match) * [string_nested_split](#string_nested_split) @@ -40,16 +38,13 @@ So I have created somewhat alot of functions which does things that you might ne * [string_normalize_index](#string_normalize_index) * [string_overlap](#string_overlap) * [string_pad](#string_pad) -* [string_parentheses_encode](#string_parentheses_encode) -* [string_parentheses_encode](#string_parentheses_encode) +* [string_random](#string_random) * [string_regex_escape](#string_regex_escape) * [string_remove_beginning](#string_remove_beginning) * [string_remove_ending](#string_remove_ending) * [string_repeat](#string_repeat) * [string_replace](#string_replace) * [string_replace_first](#string_replace_first) -* [string_semicolon_decode](#string_semicolon_decode) -* [string_semicolon_encode](#string_semicolon_encode) * [string_shorten](#string_shorten) * [string_slice](#string_slice) * [string_split](#string_split) @@ -65,6 +60,7 @@ So I have created somewhat alot of functions which does things that you might ne * [string_take_regex](#string_take_regex) * [string_take_whitespace](#string_take_whitespace) * [string_tolower](#string_tolower) +* [string_toupper](#string_toupper) * [string_trim](#string_trim) * [string_trim_to_difference](#string_trim_to_difference) @@ -113,6 +109,12 @@ So I have created somewhat alot of functions which does things that you might ne +## `argument_escape` + + + + + ## `format` [**`format(