Compare commits

...

212 commits

Author SHA1 Message Date
Joey Yakimowich-Payne
638c9b4a5a Fix bug with length 2019-09-07 09:12:32 -06:00
Joey Yakimowich-Payne
942ae8e803 Merge remote-tracking branch 'upstream/master' 2019-09-07 08:51:49 -06:00
Ganesh Viswanathan
2be81614a9 Fix #42 - curl should follow redirects, powershell wget backport 2019-07-16 22:17:32 -05:00
Ganesh Viswanathan
39f5f320a6 Remove libsvm 2019-07-16 22:06:06 -05:00
Ganesh Viswanathan
fabd615379 C2nim is now backwards compatible 2019-07-16 14:06:02 -05:00
Ganesh Viswanathan
b54feea3fc Remove nimnuklear 2019-07-16 13:04:05 -05:00
Ganesh Viswanathan
4ab5ff93b8 Fix c2nim dependency 2019-07-15 22:49:04 -05:00
Ganesh Viswanathan
97141f523f Bump to latest 2019-07-09 23:58:01 -05:00
Joey Yakimowich-Payne
d6f8d140cc Add function to remove function bodies 2019-03-22 09:53:23 -06:00
Joey Yakimowich-Payne
69b9ee9083 Re-enable remove static 2019-03-22 09:15:49 -06:00
Ganesh Viswanathan
293476c0a9 Remove nimarchive since it is currently broken 2019-03-06 00:34:11 -06:00
Ganesh Viswanathan
cf132951bd Update appveyor to 0.19.2 2018-12-31 12:33:09 -06:00
Ganesh Viswanathan
31b63a6ea6 Update nimfuzz to nimfuzzy 2018-12-10 12:00:53 -06:00
genotrance
5199b86b74
Merge pull request #39 from data-man/ospaths
Don't use ospaths on Nim-devel
2018-12-10 09:49:25 -06:00
Ganesh Viswanathan
46cce39e64 Use choosenim for Travis 2018-12-10 09:48:46 -06:00
data-man
2f1a62a030 Don't use ospaths on Nim-devel 2018-12-09 05:54:36 +05:00
Ganesh Viswanathan
24c62dbc18 Enable nimfastText on OSX 2018-12-07 17:58:46 -06:00
Ganesh Viswanathan
86df7b19f1 Fix README 2018-11-08 21:39:20 -06:00
Ganesh Viswanathan
d6241b1da4 Pygmentize only 2018-11-04 16:19:54 -06:00
Ganesh Viswanathan
02459f3d82 Remove nimfastText from gcc < 5.x 2018-11-04 15:06:37 -06:00
Ganesh Viswanathan
b78f6d38fd Fix nimgen dir regression 2018-11-04 14:34:50 -06:00
Ganesh Viswanathan
44fc30a9cb Add nimdoc.cfg 2018-11-04 14:27:27 -06:00
Ganesh Viswanathan
ae6d4aff28 Last nightlies release 2018-11-03 23:38:04 -05:00
Ganesh Viswanathan
47ca2cffd6 Brew no autoupdate 2018-11-03 23:20:28 -05:00
Ganesh Viswanathan
c60a8cd415 Remove nimzbar from OSX 2018-11-03 21:14:13 -05:00
Ganesh Viswanathan
5be86fcd99 Brew install 2018-11-02 23:24:45 -05:00
Ganesh Viswanathan
a8b836656e libssh2 for OSX 2018-11-02 23:04:58 -05:00
Ganesh Viswanathan
81fa6d15ba Github download 2018-11-02 22:26:46 -05:00
Ganesh Viswanathan
d6c827bb28 Github download 2018-11-02 22:18:05 -05:00
Ganesh Viswanathan
03c38a2448 Github download 2018-11-02 22:04:09 -05:00
Ganesh Viswanathan
d080be55a5 Github download 2018-11-02 21:56:14 -05:00
Ganesh Viswanathan
d27d28f0d9 Fix travis again 2018-11-02 17:15:35 -05:00
Ganesh Viswanathan
c7c419e6fa Backrev c2nim 2018-11-02 17:12:37 -05:00
Ganesh Viswanathan
5549505f61 Cleanup 2018-11-02 15:09:28 -05:00
Ganesh Viswanathan
34efd6619f Docgen only on Windows 2018-11-02 14:14:15 -05:00
Ganesh Viswanathan
715b502274 Fix path 2018-11-02 11:25:48 -05:00
Ganesh Viswanathan
c40919b87c Fix deps 2018-11-02 10:58:38 -05:00
Ganesh Viswanathan
713703caae Travis fixes 2018-11-02 10:55:05 -05:00
Ganesh Viswanathan
ee7a55f2ec Add Travis for devel/OSX 2018-11-02 10:38:38 -05:00
Ganesh Viswanathan
f1a8d17891 Fix version print 2018-11-01 15:37:56 -05:00
Ganesh Viswanathan
8188c75118 Enable 0.19.0 2018-11-01 14:31:09 -05:00
Ganesh Viswanathan
ad49917733 Appveyor bugfix 2018-10-30 23:25:43 -05:00
Ganesh Viswanathan
ebd362d934 Appveyor bugfix 2018-10-30 23:04:28 -05:00
Ganesh Viswanathan
e8faaf8d0c Appveyor bugfix 2018-10-30 22:54:08 -05:00
Ganesh Viswanathan
9ec27678ea Appveyor bugfix 2018-10-30 22:43:55 -05:00
Ganesh Viswanathan
1f3ae34dc0 Fix gGitOutput set 2018-10-30 22:16:29 -05:00
Ganesh Viswanathan
1c057f2520 OS specific sections, execAction, nowildcard, multi git repos, branch/tag/commit 2018-10-30 21:55:17 -05:00
Ganesh Viswanathan
caba6fcc50 Fix regex crash 2018-10-08 17:54:43 -05:00
Ganesh Viswanathan
da865618be Multi-version CI 2018-10-08 17:28:58 -05:00
Ganesh Viswanathan
b5030a8bd5 Multi-version CI 2018-10-08 17:27:07 -05:00
Ganesh Viswanathan
c4d45b318b Multi-version CI 2018-10-08 17:17:33 -05:00
Ganesh Viswanathan
35765f046b Multi-version CI 2018-10-08 12:28:12 -05:00
Ganesh Viswanathan
db26f9580d Multi-version CI 2018-10-08 12:21:49 -05:00
Ganesh Viswanathan
83465b25de Multi-version CI 2018-10-08 12:15:01 -05:00
Ganesh Viswanathan
efc04c38cc Multi-version CI 2018-10-08 12:08:38 -05:00
Ganesh Viswanathan
e04b40d6a8 Remove nimfastText 2018-10-05 10:47:36 -05:00
Ganesh Viswanathan
4df59320fd Comp fixes 2018-10-04 21:17:02 -05:00
Ganesh Viswanathan
6b16a83572 Edit comps 2018-10-04 17:10:43 -05:00
Ganesh Viswanathan
0a32e7fa2c Add nimmonocrypt, remove nimfastText 2018-10-02 12:24:28 -05:00
Ganesh Viswanathan
2f27ad0d05 Save artifacts 2018-09-27 19:27:22 -05:00
Ganesh Viswanathan
6c8bf5e842 Save artifacts 2018-09-27 19:06:16 -05:00
Ganesh Viswanathan
598214c1d2 Save project artifacts 2018-09-27 18:35:11 -05:00
Ganesh Viswanathan
8eaa765601 Add nimnuklear and nimfastText 2018-09-26 16:44:28 -05:00
Ganesh Viswanathan
23bb86803d Add duktape-nim 2018-09-05 14:33:35 -05:00
Ganesh Viswanathan
7082f89781 Add duktape-nim 2018-09-05 14:32:19 -05:00
Ganesh Viswanathan
a2a2c41704 Add nimtess2 to test suite 2018-09-05 13:18:45 -05:00
Ganesh Viswanathan
864506afbc Remove ropes dependency 2018-08-19 19:25:04 -05:00
Ganesh Viswanathan
02de68b36d Fix nil rope 2018-08-19 17:05:19 -05:00
Ganesh Viswanathan
1711052eae Add move capability, handle compile duplicate names 2018-08-19 12:31:21 -05:00
Ganesh Viswanathan
7e21b2799e Improve compile 2018-08-15 15:19:59 -05:00
Ganesh Viswanathan
1e26d3e6eb More flexibility in compile flag 2018-08-15 14:41:41 -05:00
Ganesh Viswanathan
1a29b9f0f8 Add badges 2018-08-14 11:21:51 -05:00
Ganesh Viswanathan
1fb8674d07 Add badges 2018-08-14 11:19:32 -05:00
Ganesh Viswanathan
bb2c9474fa Add badges 2018-08-14 11:18:31 -05:00
Ganesh Viswanathan
667d6caf84 CLI support - #20 2018-08-08 11:06:21 -05:00
Ganesh Viswanathan
0406d83384 Merge branch 'master' of https://github.com/genotrance/nimgen into features-1 2018-08-06 11:37:02 -05:00
genotrance
db0363505e
Merge pull request #34 from jyapayne/fix_unittest_continue
Fix unit test for Windows and quit with failure code
2018-08-06 11:36:11 -05:00
Ganesh Viswanathan
32debc5534 Regex search/replace support - #2 2018-08-05 01:10:47 -05:00
Ganesh Viswanathan
1dbff5cbd1 Inline fixes 2018-08-03 23:27:26 -05:00
Joey Yakimowich-Payne
c6148571c1 Fix windows echo 2018-08-03 15:08:03 +09:00
Joey Yakimowich-Payne
87e08bfb66 Fix unit test for Windows and quit with failure code 2018-08-03 13:02:56 +09:00
Ganesh Viswanathan
2c3cc71540 Fix permission denied and file flushing issues 2018-08-02 20:49:50 -05:00
genotrance
dc9943a22c
Merge pull request #31 from jyapayne/fix_another_issue
Fix windows not processing headers
2018-07-18 09:28:10 -05:00
Joey Yakimowich-Payne
f3c01e53f9 Fix a few more issues with windows paths 2018-07-18 12:25:13 +09:00
Joey Yakimowich-Payne
6343567a91 Put sanitizePath in search() 2018-07-18 07:52:45 +09:00
Joey Yakimowich-Payne
5e3c0999e0 Fix windows not processing headers 2018-07-17 18:11:39 +09:00
genotrance
aad005e0b1
Merge pull request #30 from jyapayne/fix_cross_compile_sourcepath
Fix issue with sourcepath building switch on Windows
2018-07-16 08:48:59 -05:00
Joey Yakimowich-Payne
0b2fb6cfbc Fix issue with sourcpath building switch on Windows 2018-07-15 15:40:00 +09:00
genotrance
96949bf150
Merge pull request #28 from data-man/multireplace
More multiReplaces
2018-07-15 01:18:22 -05:00
data-man
f8f9186429 Use sanitizePath 2018-07-15 07:49:05 +03:00
data-man
43320e31a1 Merge branch 'master' of https://github.com/genotrance/nimgen into multireplace 2018-07-15 07:47:10 +03:00
genotrance
6b38a55803
Merge pull request #27 from jyapayne/fix_windows_issues
Fix miscellaneous windows issues with building
2018-07-14 23:31:01 -05:00
data-man
2d06bdcccf More multiReplaces 2018-07-15 04:49:24 +03:00
Joey Yakimowich-Payne
ffc73c6b6d Prevent processed file collisions 2018-07-15 10:06:46 +09:00
Joey Yakimowich-Payne
0473e79c0b Fix miscellaneous windows issues with building 2018-07-14 22:04:03 +09:00
genotrance
2fae745378
Merge pull request #26 from jyapayne/add_unit_tests
Add tests for fileops
2018-07-14 00:52:56 -05:00
Joey Yakimowich-Payne
de4cb3f783 Add tests for fileops 2018-07-14 10:24:53 +09:00
Ganesh Viswanathan
29d694d630 Implement #11 - comma separated for OS specific tasks 2018-07-13 20:06:09 -05:00
genotrance
28a803e2cb
Merge pull request #25 from data-man/multireplace
Use multiReplace in some places
2018-07-13 14:58:27 -05:00
genotrance
573fe9f217
Merge pull request #24 from data-man/simple_split
Use strutils.split
2018-07-13 14:08:44 -05:00
Ganesh Viswanathan
0eaaf2e1fe Fix base dir 2018-07-13 13:36:15 -05:00
Ganesh Viswanathan
d1aaf70e25 Cache Linux binary 2018-07-13 13:12:46 -05:00
Ganesh Viswanathan
2cfad92753 Fix file creation issue 2018-07-13 12:51:27 -05:00
data-man
2e332b29af Use multiReplace in some places 2018-07-13 13:33:43 +03:00
data-man
04cd7055cf Use strutils.split 2018-07-13 12:52:11 +03:00
Ganesh Viswanathan
9e0eb25685 Implement #10 - per file reset 2018-07-13 02:16:01 -05:00
Ganesh Viswanathan
1a2ef87087 Skip files already done 2018-07-13 01:37:50 -05:00
Ganesh Viswanathan
5fc18f82f4 Merge branch 'master' of https://github.com/genotrance/nimgen 2018-07-13 00:20:15 -05:00
Ganesh Viswanathan
e46d3b2332 Implement #7 - [n.sourcefile] section, add nimclipboard 2018-07-13 00:19:50 -05:00
genotrance
0c13c06cfe
Merge pull request #22 from jyapayne/temp_regex_fix
Workaround regex replace issue
2018-07-12 21:59:40 -05:00
Joey Yakimowich-Payne
59c64782bd Workaround regex replace issue 2018-07-13 08:17:10 +09:00
Ganesh Viswanathan
7d60e2eea7 Update version to v0.3.0 2018-07-12 16:37:31 -05:00
Ganesh Viswanathan
4b761bebd4 Fix rename bug, additional cleanup 2018-07-12 14:39:24 -05:00
Ganesh Viswanathan
562f3fd7eb Fix directory exists error 2018-07-12 01:47:22 -05:00
Ganesh Viswanathan
cf629494a4 Split into multiple files 2018-07-12 01:36:15 -05:00
Ganesh Viswanathan
293e8cca85 Minor updates 2018-07-11 23:18:00 -05:00
Ganesh Viswanathan
bb4073ba97 Merge latest 2018-07-11 23:03:14 -05:00
Ganesh Viswanathan
2766607277 Merge branch 'master' of https://github.com/genotrance/nimgen into regex-1 2018-07-11 22:59:16 -05:00
Ganesh Viswanathan
e32acea78e Move from nre to regex 2018-07-11 22:37:06 -05:00
genotrance
6aca40808b
Merge pull request #19 from jyapayne/add_static_remove
Add preliminary support for removing static inline function bodies
2018-07-11 00:04:36 -05:00
Joey Yakimowich-Payne
15ba479468 Remove removestatic from readme 2018-07-11 13:54:37 +09:00
Joey Yakimowich-Payne
685081b6a9 Modify removeStatic to comment out body, then re-comment 2018-07-11 13:36:45 +09:00
Joey Yakimowich-Payne
f62125a4aa Update readme with removestatic 2018-07-11 07:53:05 +09:00
Joey Yakimowich-Payne
14b119f433 Move remove static and reset after processing 2018-07-11 07:36:40 +09:00
genotrance
ef59bd105d
Merge pull request #14 from jyapayne/reset_files
Add section for resetting files
2018-07-10 12:30:56 -05:00
Joey Yakimowich-Payne
0d0506675c Make static bodies replacement use regex 2018-07-10 20:19:51 +09:00
genotrance
9c2c252f04
Merge pull request #18 from jyapayne/allow_relative_imports
Implement relative import support in header files.
2018-07-10 01:57:39 -05:00
Joey Yakimowich-Payne
f5e8535ce8 Fix windows paths 2018-07-10 15:22:50 +09:00
Joey Yakimowich-Payne
ab3185b611 Add n.post section 2018-07-10 15:15:00 +09:00
Joey Yakimowich-Payne
2e894dc907 Implement relative import support in header files.
Fixes #17
2018-07-10 08:45:58 +09:00
Joey Yakimowich-Payne
5321014983 Add readme section about remove_static 2018-07-10 08:41:06 +09:00
Joey Yakimowich-Payne
b32d39ad08 Add preliminary support for removing static funcs 2018-07-10 08:41:06 +09:00
genotrance
84894bfa0c
Merge pull request #4 from jyapayne/configurable_compiler
Add configurable compiler to global section
2018-07-09 15:40:33 -05:00
Ganesh Viswanathan
adaa7e997f Setup CI 2018-07-09 15:20:31 -05:00
Ganesh Viswanathan
a24d0d043d Setup CI 2018-07-09 15:10:02 -05:00
Ganesh Viswanathan
0e6a7b8025 Setup CI 2018-07-09 15:03:41 -05:00
Ganesh Viswanathan
5621d59e3c Setup CI 2018-07-09 15:01:58 -05:00
Ganesh Viswanathan
942f194945 Setup CI 2018-07-09 14:27:20 -05:00
Ganesh Viswanathan
0e016bd7bb Merge branch 'master' of https://github.com/genotrance/nimgen 2018-07-09 14:09:45 -05:00
Ganesh Viswanathan
1f2bd20da1 Setup CI 2018-07-09 14:09:38 -05:00
Ganesh Viswanathan
b894dbd17a Allow ppflags and related to be OS specific 2018-07-09 14:02:38 -05:00
Ganesh Viswanathan
217161bb4d Setup CI 2018-07-09 12:26:58 -05:00
Ganesh Viswanathan
6aa9b4290a Setup CI 2018-07-09 12:18:08 -05:00
Ganesh Viswanathan
9fcd0ddd0f Setup CI 2018-07-09 12:14:55 -05:00
Ganesh Viswanathan
cc7b6cb726 Setup CI 2018-07-09 12:09:55 -05:00
Ganesh Viswanathan
9257a0a81b Setup CI 2018-07-09 12:06:07 -05:00
Ganesh Viswanathan
7ae458419e Setup CI 2018-07-09 11:51:08 -05:00
Ganesh Viswanathan
894818a0db Setup CI 2018-07-09 11:47:13 -05:00
Ganesh Viswanathan
6f36dcb99d Setup CI 2018-07-09 11:45:09 -05:00
Ganesh Viswanathan
7c325683cc Setup CI 2018-07-09 11:42:01 -05:00
Ganesh Viswanathan
88d8ee6b84 Setup CI 2018-07-09 11:18:14 -05:00
Ganesh Viswanathan
37fdefa4b8 Setup CI 2018-07-09 10:29:50 -05:00
Ganesh Viswanathan
9821b228af Setup CI 2018-07-09 10:26:18 -05:00
Ganesh Viswanathan
7979cec722 Setup CI 2018-07-09 10:13:12 -05:00
Ganesh Viswanathan
23d0016d1a Setup CI 2018-07-09 09:42:55 -05:00
Ganesh Viswanathan
941df23278 Setup CI 2018-07-09 09:13:15 -05:00
Ganesh Viswanathan
b8670b1bf1 Setup CI 2018-07-09 09:11:25 -05:00
Ganesh Viswanathan
c7012369be Setup CI 2018-07-08 22:14:22 -05:00
Ganesh Viswanathan
395422f1e5 Setup CI 2018-07-08 22:09:41 -05:00
Ganesh Viswanathan
fdcbec8d5b Setup CI 2018-07-08 21:44:36 -05:00
Ganesh Viswanathan
8892eabbe6 Setup CI 2018-07-08 21:33:01 -05:00
Ganesh Viswanathan
1aab352f03 Setup CI 2018-07-08 21:31:09 -05:00
Ganesh Viswanathan
30e5da82a0 Setup CI 2018-07-08 21:14:23 -05:00
Ganesh Viswanathan
ad7df8d34e Setup CI 2018-07-08 21:12:52 -05:00
Ganesh Viswanathan
0226dba427 Setup CI 2018-07-08 21:06:28 -05:00
Joey Yakimowich-Payne
8e8e5fbd09 Change consts to camel case for nep1 2018-07-09 09:52:23 +09:00
Joey Yakimowich-Payne
3b60a0e4b1 Move env var section in readme 2018-07-09 09:51:44 +09:00
Joey Yakimowich-Payne
9c41b5a176 Add better example to readme for env vars 2018-07-09 09:21:14 +09:00
Joey Yakimowich-Payne
17e54d3b87 Make c/cpp compiler priority .cfg > CC/CXX > gcc/g++ 2018-07-09 09:16:44 +09:00
Ganesh Viswanathan
60d94e9114 Fix for absolute paths in includes and headers 2018-07-08 19:11:41 -05:00
Ganesh Viswanathan
a717b9d140 Update version 2018-07-08 16:03:13 -05:00
Ganesh Viswanathan
0bda8084dd Merge branch 'jyapayne-fix_includes' 2018-07-08 16:02:16 -05:00
Ganesh Viswanathan
c846bf106a Fixes to work with all wrappers 2018-07-08 16:01:54 -05:00
Joey Yakimowich-Payne
512cdd3a9f Add check for absolute directories 2018-07-08 22:56:36 +09:00
Joey Yakimowich-Payne
15d417d2c5 Remove dir sep from replace 2018-07-08 22:50:34 +09:00
Joey Yakimowich-Payne
4c45a5f5b2 Combine passC and absolute imports 2018-07-08 22:43:17 +09:00
Joey Yakimowich-Payne
76020e8f21 Add env vars to readme 2018-07-08 13:05:36 +09:00
Joey Yakimowich-Payne
2fc12b8577 Only include necessary file for include libs
The previous behavior caused a "Too many files open" error when
referrencing lots of libs because of the call to "gorge". This
modification retains the old behavior and also works if the library is
compiled locally.

Fixes #12
2018-07-08 12:39:27 +09:00
Joey Yakimowich-Payne
467d0c4eae Add ability to use environment vars 2018-07-08 12:08:43 +09:00
Joey Yakimowich-Payne
bfd9e50652 Merge remote-tracking branch 'upstream/master' into configurable_compiler 2018-07-08 10:59:49 +09:00
Ganesh Viswanathan
44294a7513 Readme fix 2018-07-07 17:47:55 -05:00
Ganesh Viswanathan
a0369758af Support 0.17.0 onwards, fix IOError 2018-07-07 16:57:23 -05:00
Ganesh Viswanathan
bb881a2e65 Fix IOErrors 2018-07-07 15:03:14 -05:00
genotrance
1b645dd5b2
Merge pull request #6 from jyapayne/add_execute_for_files
Add support for executing a command on a file.
2018-07-07 14:30:21 -05:00
Joey Yakimowich-Payne
a7edd8525d Rename execute to pipe 2018-06-27 22:22:38 +09:00
genotrance
d943f117c4
Merge pull request #5 from jyapayne/allow_deeper_output
Allow for deeper than one directory paths for the output directory
2018-06-23 19:23:33 -05:00
Joey Yakimowich-Payne
4a83f6d5a1 Fix issue if cfg file is local gProject dir will be an empty string 2018-06-24 08:21:50 +09:00
Joey Yakimowich-Payne
2f2b26d269 Change project dir based on nimgen cfg file 2018-06-24 07:50:47 +09:00
Ganesh Viswanathan
88a57e5452 Fix #2 - ctags not working 2018-06-23 12:08:48 -05:00
Joey Yakimowich-Payne
e371546d52 Add support for executing a command on a file.
$file in the command string references the file name. The stdout of the
command will become the file's new contents. This is useful for doing
things that nimgen might not support, like using sed or grep or some
other command line tool.
2018-06-16 15:33:47 +09:00
Joey Yakimowich-Payne
71699332be Allow for deeper than one directory paths 2018-06-16 15:22:16 +09:00
Joey Yakimowich-Payne
f275bc0e35 Update readme with compiler spec 2018-06-16 15:17:51 +09:00
Joey Yakimowich-Payne
0f73ad6d3f Add configurable compiler to global section 2018-06-14 17:13:46 +09:00
Ganesh Viswanathan
a8bcbf8e89 Revert nep1 for c2nim 2018-06-11 11:00:51 -05:00
Ganesh Viswanathan
9805bdbc80 Internal cleanup 2018-06-11 10:38:29 -05:00
Ganesh Viswanathan
e3a3fe12e6 Bugfix for compile 2018-06-11 09:50:30 -05:00
Ganesh Viswanathan
4b91f1bbbe Copy files during prepare, NEP1 conformance 2018-06-11 09:42:41 -05:00
Ganesh Viswanathan
7fbc8fbcde Style fixes 2018-06-11 01:47:08 -05:00
Ganesh Viswanathan
5e784f09ef Add support for renaming generated files 2018-06-09 17:58:39 -05:00
Ganesh Viswanathan
df54942933 Add nimlibxlsxwriter to README 2018-05-04 16:42:19 -05:00
Ganesh Viswanathan
b8d6696850 Links to wrapper docs 2018-05-04 16:30:11 -05:00
Ganesh Viswanathan
335e3ac28a Generate documentation 2018-05-04 16:10:03 -05:00
Ganesh Viswanathan
6d0abefe8a Minor bugfixes 2018-03-13 17:05:40 -05:00
Ganesh Viswanathan
fec0e49fac Added nimrax 2018-03-08 23:43:58 -06:00
Ganesh Viswanathan
bcace601d3 Comment lines feature, remove docopt 2018-03-08 22:28:41 -06:00
Ganesh Viswanathan
968e89f08e Added inlining in place of recursion, extract existing ZIP file without downloading, delete/create output directory error handling 2018-02-02 14:48:59 -06:00
Ganesh Viswanathan
9b25a6d1db Typo 2018-01-05 20:14:17 -06:00
Ganesh Viswanathan
53f0626bed Support for pragma definitions 2018-01-05 19:16:49 -06:00
Ganesh Viswanathan
6a64ff568d README updates 2017-12-04 17:29:29 -06:00
Ganesh Viswanathan
2ae534476f Readme updates 2017-12-04 14:29:38 -06:00
Ganesh Viswanathan
3a6c74afb7 Merge branch 'master' of https://github.com/genotrance/nimgen 2017-12-04 14:26:44 -06:00
Ganesh Viswanathan
27ea25b901 - Added support for wildcard file tasks
- Fixed issue in replacing - with _ in generated modules
- Fixed inconsistent naming of flags and procs
2017-12-04 14:26:20 -06:00
26 changed files with 4712 additions and 571 deletions

3
.gitignore vendored
View file

@ -1,4 +1,5 @@
nimcache nimcache
nimgen nimgen
web
*.exe *.exe
*.swp *.swp

27
.travis.yml Normal file
View file

@ -0,0 +1,27 @@
os:
- linux
- osx
language: c
env:
- BRANCH=devel
addons:
apt:
packages:
- libssh2-1-dev
before_install:
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then HOMEBREW_NO_AUTO_UPDATE=1 brew install libssh2; fi
install:
- export CHOOSENIM_CHOOSE_VERSION=$BRANCH
- |
curl https://nim-lang.org/choosenim/init.sh -sSf > init.sh
sh init.sh -y
- export PATH=$HOME/.nimble/bin:$PATH
script:
- nimble install -y
- nimble test

187
README.md
View file

@ -1,4 +1,8 @@
Nimgen is a helper for [c2nim](https://github.com/nim-lang/c2nim/) to simpilfy and automate the wrapping of C libraries. [![Chat on Gitter](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/nimgen/Lobby)
[![Build status](https://ci.appveyor.com/api/projects/status/05t5ja88lmv1rt3r/branch/master?svg=true)](https://ci.appveyor.com/project/genotrance/nimgen/branch/master)
[![Build Status](https://travis-ci.org/genotrance/nimgen.svg?branch=master)](https://travis-ci.org/genotrance/nimgen)
Nimgen is a helper for [c2nim](https://github.com/nim-lang/c2nim/) to simplify and automate the wrapping of C libraries.
Nimgen can be used to automate the process of manipulating C files so that c2nim can be run on them without issues. This includes adding/removing code snippets, removal of complex preprocessor definitions that c2nim doesn't yet comprehend and recursively running on #include files. Nimgen can be used to automate the process of manipulating C files so that c2nim can be run on them without issues. This includes adding/removing code snippets, removal of complex preprocessor definitions that c2nim doesn't yet comprehend and recursively running on #include files.
@ -6,13 +10,13 @@ __Installation__
Nimgen can be installed via [Nimble](https://github.com/nim-lang/nimble): Nimgen can be installed via [Nimble](https://github.com/nim-lang/nimble):
```> nimble install https://github.com/genotrance/nimgen``` ```> nimble install nimgen```
This will download, build and install nimgen in the standard Nimble package location, typically ~/.nimble. Once installed, it can be run just like c2nim. This will download, build and install nimgen in the standard Nimble package location, typically ~/.nimble. Once installed, it can be run just like c2nim.
__Usage__ __Usage__
Nimgen is driven by a simple .cfg file that is read using the Nim [parsecfg](https://nim-lang.org/docs/parsecfg.html) module. The sections of the file are described further below. Nimgen is driven by a simple .cfg file that is read using the Nim [parsecfg](https://nim-lang.org/docs/parsecfg.html) module. The sections of the file are described further below.
```> nimgen package.cfg``` ```> nimgen package.cfg```
@ -28,14 +32,68 @@ Nimble already requires Git so those commands can be assumed to be present to do
__Capabilities & Limitations__ __Capabilities & Limitations__
Nimgen supports compiling in C/C++ sources as well as loading in dynamic libraries at this time. Support for static libraries (.a, .lib) are still to come. Nimgen supports compiling in C/C++ sources and static libraries as well as loading in dynamic libraries.
To see examples of nimgen in action, the [nimssl](https://github.com/genotrance/nimssl) library is a good example of C code getting compiled in whereas [nimbass](https://github.com/genotrance/nimbass) is an example of linking with a dynamic library. To see examples of nimgen in action check out the following wrappers:-
* Link with a dynamic library
* [nimbass](https://github.com/genotrance/nimbass) - BASS audio wrapper: [docs](http://nimgen.genotrance.com/nimbass)
* download ZIP with headers and binary
* [nimlibxlsxwriter](https://github.com/KeepCoolWithCoolidge/nimlibxlsxwriter) - libxlsxwriter wrapper
* git checkout
* [nim-clblast](https://github.com/numforge/nim-clblast) - OpenCL BLAS wrapper
* static
Nimgen only supports the ```gcc``` preprocessor at this time. Support for detecting and using other preprocessors is TBD. * Compile C/C++ code into binary
* [nim7z](https://github.com/genotrance/nim7z) - 7z decoder wrapper: [docs](http://nimgen.genotrance.com/nim7z)
* git sparse checkout
* [nimarchive](https://github.com/genotrance/nimarchive) - libarchive wrapper: [docs](http://nimgen.genotrance.com/nimarchive)
* git sparse checkout
* [nimbigwig](https://github.com/genotrance/nimbigwig) - libbigWig wrapper: [docs](http://nimgen.genotrance.com/nimbigwig)
* git checkout
* [nimclipboard](https://github.com/genotrance/nimclipboard) - libclipboard wrapper: [docs](http://nimgen.genotrance.com/nimclipboard)
* git checkout
* [nimfastText](https://github.com/genotrance/nimfastText) - fastText wrapper: [docs](http://nimgen.genotrance.com/nimfastText)
* git sparse checkout
* [nimfuzzy](https://github.com/genotrance/nimfuzzy) - fts_fuzzy_match wrapper: [docs](http://nimgen.genotrance.com/nimfuzzy)
* download header file
* [nimkerberos](https://github.com/genotrance/nimkerberos) - WinKerberos wrapper: [docs](http://nimgen.genotrance.com/nimkerberos)
* git sparse checkout
* [nimmonocypher](https://github.com/genotrance/nimmonocypher) - monocypher wrapper: [docs](http://nimgen.genotrance.com/nimmonocypher)
* git sparse checkout
* [nimnuklear](https://github.com/genotrance/nimnuklear) - nuklear wrapper: [docs](https://nimgen.genotrance.com/nimnuklear)
* git sparse checkout
* [nimpcre](https://github.com/genotrance/nimpcre) - PCRE wrapper: [docs](http://nimgen.genotrance.com/nimpcre)
* git checkout
* [nimrax](https://github.com/genotrance/nimrax) - Radix tree wrapper: [docs](http://nimgen.genotrance.com/nimrax)
* git checkout
* [nimssl](https://github.com/genotrance/nimssl) - OpenSSL wrapper: [docs](http://nimgen.genotrance.com/nimssl)
* git sparse checkout
* [nimtess2](https://github.com/genotrance/nimtess2) - libtess2 wrapper: [docs](http://nimgen.genotrance.com/nimtess2)
* git checkout
* [duktape-nim](https://github.com/manguluka/duktape-nim) - Duktape wrapper
* static
* Compile in as static binary
* [nimssh2](https://github.com/genotrance/nimssh2) - libssh2 wrapper: [docs](http://nimgen.genotrance.com/nimssh2)
* git sparse checkout
Nimgen only supports the ```gcc``` preprocessor at this time. Support for detecting and using other preprocessors will be based on interest.
__Config file__ __Config file__
In all sections below, environment variables are supported via Nim's string interpolation `%` symbol imported from the `strutils` module. Simply use double quotes to enclose any value and put `$` or `${}` around the environment variable name. In addition, the `output` var from the n.global section is available as ${output}. For example:
[n.global]
c_compiler="$CC"
cpp_compiler="${CPP}-arm"
output="src/path"
[n.include]
"${output}/library/include"
"${MY_INCLUDE_PATH}/include"
Append -win, -lin and -mac/osx for OS specific sections and tasks. E.g. n.global-win, n.post-lin, download-win, execute-lin-mac.unique1. -unix can be used as a shortcut for -lin-mac.
_[n.global]_ _[n.global]_
```output``` = name of the Nimble project once installed, also location to place generated .nim files ```output``` = name of the Nimble project once installed, also location to place generated .nim files
@ -44,6 +102,10 @@ _[n.global]_
```filter``` = string to identify and recurse into library .h files in #include statements and exclude standard headers ```filter``` = string to identify and recurse into library .h files in #include statements and exclude standard headers
```cpp_compiler``` = string to specify a CPP compiler executable. [default: g++]
```c_compiler``` = string to specify a C compiler executable. [default: gcc]
_[n.include]_ _[n.include]_
List of all directories, one per line, to include in the search path. This is used by:- List of all directories, one per line, to include in the search path. This is used by:-
@ -58,24 +120,54 @@ List of all directories or files to exclude from all parsing. If an entry here m
_[n.prepare]_ _[n.prepare]_
The following keys can be used to prepare dependencies such as downloading ZIP files, cloning Git repositories, etc. Multiple entries are possible by appending any .string to the key. E.g. download.file1. -win, -lin and -osx can be used for OS specific tasks. E.g. download-win The following keys can be used to prepare dependencies such as downloading ZIP files, cloning Git repositories, etc. Multiple entries are possible by appending any .string to the key. E.g. download.file1.
```download``` = url to download to the output directory. ZIP files are automatically extracted. Files are not redownloaded if already present but re-extracted ```download``` = url to download to the output directory. ZIP files are automatically extracted. Files are not redownloaded if already present but re-extracted
```git``` = url of Git repository to clone. Full repo is pulled so gitremote + gitsparse is preferable. Resets to HEAD if already present ```extract``` = ZIP file to extract in case they are local and don't need to be downloaded. Path is relative to output directory.
```gitcheckout``` = branch, commit or tag of repository to checkout in following Git command, resets after each use. Use "-b name" for branches
```gitoutput``` = directory for all following Git commands relative to `n.global:output` [default: `n.global:output` directory]
```git``` = url of Git repository to clone. Full repo is pulled so gitremote + gitsparse is preferable. Resets if already present
```gitremote``` = url of Git repository to partially checkout. Use with gitsparse to pull only files and dirs of interest ```gitremote``` = url of Git repository to partially checkout. Use with gitsparse to pull only files and dirs of interest
```gitsparse``` = list of files and/or dirs to include in partial checkout, one per line. Resets to HEAD if already present ```gitsparse``` = list of files and/or dirs to include in partial checkout, one per line. Resets if already present
```execute``` = command to run during preparation ```execute``` = command to run during preparation
```copy``` = copy a file to another location. Preferred over moving to preserve original. Comma separate for multiple entries. E.g. copy = "output/config.h.in=output/config.h"
_[n.post]_
This section is the same as the prepare section, but for performing actions after the project has been processed.
```gitoutput``` = output directory for Git reset [default: `n.global:output` directory]
```reset``` = perform a Git reset on all files after processing [default: false]
```execute``` = command to run after processing
_[n.wildcard]_
File wildcards such as *.nim, ssl*.h, etc. can be used to perform tasks across a group of files. This is useful to define common operations such as global text replacements without having to specify an explicit section for every single file. These operations will be performed on every matching file that is defined as a _sourcefile_ or recursed files. Only applies on source files following the wildcard declarations.
```wildcard``` = pattern to match against. All keys following the wildcard declaration will apply to matched files
_[n.sourcefile]_
This section allows selection of multiple sourcefiles without requiring a detailed section for each file. Each specific file can be listed one line at a time and file wildcards can be used to include multiple source files. E.g. `$output/include/*/h`. `[n.wildcard]` definitions can be used to perform common operations on these source files if required.
_[sourcefile]_ _[sourcefile]_
The following keys apply to library source code and help with generating the .nim files. -win, -lin and -osx can be used for OS specific tasks. E.g. dynlib-win The following keys apply to library source code and help with generating the .nim files. -win, -lin and -osx can be used for OS specific tasks. E.g. dynlib-win, pragma-win.
```recurse``` = find #include files and process them [default: false] ```recurse``` = find #include files and process them [default: false]
```inline``` = include #include files into file being processed, alternative method to processing each header file separately with recurse. Multiple source files will get combined into the same .nim output files [default: false]
```preprocess``` = run preprocessor (gcc -E) on file to remove #defines, etc. [default: false] - this is especially useful when c2nim doesn't support complex preprocessor usage ```preprocess``` = run preprocessor (gcc -E) on file to remove #defines, etc. [default: false] - this is especially useful when c2nim doesn't support complex preprocessor usage
```ctags``` = run ctags on file to filter out function definitions [default: false] - this requires the ctags executable and is an alternative to filter out preprocessor complexity ```ctags``` = run ctags on file to filter out function definitions [default: false] - this requires the ctags executable and is an alternative to filter out preprocessor complexity
@ -88,24 +180,89 @@ The following keys apply to library source code and help with generating the .ni
```noprocess``` = do not process this source file with c2nim [default: false] - this is useful if a file only needs to be manipulated ```noprocess``` = do not process this source file with c2nim [default: false] - this is useful if a file only needs to be manipulated
```nowildcard``` = ignore any wildcard definitions for this sourcefile
```reset``` = reset the file back to original state after all processing [default: false]
Multiple entries for the all following keys are possible by appending any .string to the key. E.g. dynlib.win, compile.dir Multiple entries for the all following keys are possible by appending any .string to the key. E.g. dynlib.win, compile.dir
```compile``` = file or dir of files of source code to {.compile.} into generated .nim ```compile``` = file or dir of files of source code to {.compile.} into generated .nim. If directory, picks *.c if C mode and *.cxx, *.cpp, *.cc, *.c++ and *.C for cpp mode. Dir can also include wildcards. e.g. compile = """dir/A*.cxx"""
```pragma``` = pragmas to define in generated .nim file. E.g. pragma = "passL: \"-lssl\"" => {.passL: "-lssl".}
```dynlib``` = dynamic library to load at runtime for generated .nim procs ```dynlib``` = dynamic library to load at runtime for generated .nim procs
The following keys apply to library source code (before processing) and generated .nim files (after processing) and allow manipulating the files as required to enable successful wrapping. The following keys apply to library source code (before processing) and generated .nim files (after processing) and allow manipulating the files as required to enable successful wrapping. They are not propagated to #include files when ```recurse = true```.
```create``` = create a file at exact location with contents specified ```create``` = create a file at exact location with contents specified. File needs to be in the _[n.exclude]_ list in order to be created.
```pipe``` = execute a command on a file and store the output of the command as the new file contents. E.g. pipe = "cat $file | grep 'static inline'"
```search``` = search string providing context for following prepend/append/replace directives ```search``` = search string providing context for following prepend/append/replace directives
```regex``` = regex search string providing context for the following replace directive. Specify using """ to avoid regex parsing issues
```prepend``` = string value to prepend into file at beginning or before search ```prepend``` = string value to prepend into file at beginning or before search
```append``` = string value to append into file at the end or after search ```append``` = string value to append into file at the end or after search
```replace``` = string value to replace search string in file ```replace``` = string value to replace search string in file. Regex captures can be referred to using $1, $2, etc.
```move``` = search string providing context for location to move the results of a preceding search or regex match
```comment``` = number of lines to comment from search location
The following key only applies before processing and allows renaming the generated .nim files as required to enable successful wrapping. This may be for organizational purposes or to prevent usage of non-nim supported strings in module names (E.g. first letter is a number). Destination is relative to output directory if defined.
```rename``` = string value to rename generated filename. E.g. rename = "$replace(7=s7)"
`/` = create a directory/module hierarchy
`$nimout` = refer to the original filename
`$replace(srch1=repl1, srch2=reply2)` = rename specific portions in `$nimout`
__Command Line__
A subset of capabilities are available through the command line to enable quick tests using nimgen. Command line flags only apply to source files specified on the command line and do not influence any ```cfg``` files which are expected to be self-sufficient.
```
Usage:
nimgen [options] file.cfg|file.h ...
Params:
-C<compile> add compile entry *
-E<exclude> add n.exclude entry *
-F<flags> set c2nim flags *
-I<include> add n.include dir *
-O<outdir> set output directory
-P<ppflags> set preprocessor flags *
Options:
-c set ctags = true
-d set defines = true
-i set inline = true
-n set noprocess = true
-p set preprocess = true
-r set recurse = true
Editing:
-a<append> append string *
-e<prepend> prepend string *
-l<replace> replace string *
-o#lines comment X lines *
-s<search> search string *
-x<regex> regex search string *
* supports multiple instances
```
__Feedback__ __Feedback__
Nimgen is a work in progress and any feedback or suggestions are welcome. It is hosted on [GitHub](https://github.com/genotrance/nimgen) with an MIT license so issues, forks and PRs are most appreciated. Nimgen is a work in progress and any feedback or suggestions are welcome. It is hosted on [GitHub](https://github.com/genotrance/nimgen) with an MIT license so issues, forks and PRs are most appreciated. Also join us at https://gitter.im/nimgen/Lobby to chat about nimgen and the future of Nim wrappers.
_Credits_
Thank you to the following contributors for their hard work!
https://github.com/jyapayne

103
appveyor.yml Normal file
View file

@ -0,0 +1,103 @@
version: '{build}'
image:
- Ubuntu
- Visual Studio 2017
matrix:
fast_finish: true
environment:
matrix:
- NIM_VERSION: 0.20.0
- NIM_VERSION: 0.19.6
for:
-
matrix:
only:
- image: Visual Studio 2017
environment:
ARCH: 32
MINGW_URL: https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win32/Personal%20Builds/mingw-builds/8.1.0/threads-posix/dwarf
MINGW_ARCHIVE: i686-8.1.0-release-posix-dwarf-rt_v6-rev0.7z
SFNET_URL: https://sourceforge.net/projects/msys2/files/REPOS/MINGW/i686
LIBSSH2_ARCHIVE: mingw-w64-i686-libssh2-1.8.0-1-any.pkg
LIBCRYPTO_ARCHIVE: mingw-w64-i686-openssl-1.0.2.o-1-any.pkg
install:
- CD c:\
- IF not exist "binaries" (
echo %NIM_VERSION% &&
MKDIR binaries &&
CD binaries &&
appveyor DownloadFile "%MINGW_URL%/%MINGW_ARCHIVE%/download" -FileName "%MINGW_ARCHIVE%" &&
7z x -y "%MINGW_ARCHIVE%"> nul &&
del "%MINGW_ARCHIVE%" &&
appveyor DownloadFile "%SFNET_URL%/%LIBSSH2_ARCHIVE%.tar.xz/download" -FileName "%LIBSSH2_ARCHIVE%.tar.xz" &&
7z x -y "%LIBSSH2_ARCHIVE%.tar.xz"> nul &&
del "%LIBSSH2_ARCHIVE%.tar.xz" &&
7z x -y "%LIBSSH2_ARCHIVE%.tar"> nul &&
del "%LIBSSH2_ARCHIVE%.tar" &&
appveyor DownloadFile "%SFNET_URL%/%LIBCRYPTO_ARCHIVE%.tar.xz/download" -FileName "%LIBCRYPTO_ARCHIVE%.tar.xz" &&
7z x -y "%LIBCRYPTO_ARCHIVE%.tar.xz"> nul &&
del "%LIBCRYPTO_ARCHIVE%.tar.xz" &&
7z x -y "%LIBCRYPTO_ARCHIVE%.tar"> nul &&
del "%LIBCRYPTO_ARCHIVE%.tar" &&
appveyor DownloadFile "https://nim-lang.org/download/nim-%NIM_VERSION%_x%ARCH%.zip" -FileName "nim-%NIM_VERSION%_x%ARCH%.zip" &&
7z x -y "nim-%NIM_VERSION%_x%ARCH%.zip"> nul &&
del "nim-%NIM_VERSION%_x%ARCH%.zip")
- SET PATH=c:\binaries\mingw%ARCH%\bin;c:\binaries\nim-%NIM_VERSION%\bin;%USERPROFILE%\.nimble\bin;%PATH%
- CD c:\projects\nimgen
on_finish:
- 7z a -r buildlogs-win-pkgs.zip %USERPROFILE%\.nimble\pkgs
- appveyor PushArtifact buildlogs-win-pkgs.zip
- 7z a -r buildlogs-win-projects.zip c:\projects\*
- appveyor PushArtifact buildlogs-win-projects.zip
- 7z a -r nimgen-docs.zip c:\projects\nimgen\web
- appveyor PushArtifact nimgen-docs.zip
cache:
- c:\binaries
-
matrix:
only:
- image: Ubuntu
install:
- sudo apt -qq update
- sudo apt -qq install --yes libssh2-1-dev libgcrypt20-dev libgpg-error-dev
- if [ ! -e /home/appveyor/binaries ]; then
echo $NIM_VERSION &&
mkdir /home/appveyor/binaries &&
cd /home/appveyor/binaries &&
curl -s -o nim-$NIM_VERSION.tar.xz https://nim-lang.org/download/nim-$NIM_VERSION.tar.xz &&
tar xJf nim-$NIM_VERSION.tar.xz &&
cd nim-$NIM_VERSION &&
sh build.sh &&
bin/nim c -d:release koch &&
./koch boot -d:release &&
./koch nimble -d:release;
fi
- export PATH=/home/appveyor/binaries/nim-$NIM_VERSION/bin:~/.nimble/bin:$PATH
- cd /home/appveyor/projects/nimgen
on_finish:
- zip -r -q buildlogs-lin-pkgs.zip ~/.nimble/pkgs
- appveyor PushArtifact buildlogs-lin-pkgs.zip
- zip -r -q buildlogs-lin-projects.zip /home/appveyor/projects
- appveyor PushArtifact buildlogs-lin-projects.zip
cache:
- /home/appveyor/binaries
build_script:
- nimble install -y
test_script:
- nimble test
deploy: off

View file

@ -1,544 +0,0 @@
import docopt
import nre
import os
import ospaths
import osproc
import parsecfg
import ropes
import sequtils
import streams
import strutils
import tables
var DONE: seq[string] = @[]
var CONFIG: Config
var FILTER = ""
var QUOTES = true
var OUTPUT = ""
var INCLUDES: seq[string] = @[]
var EXCLUDES: seq[string] = @[]
const DOC = """
Nimgen is a helper for c2nim to simpilfy and automate the wrapping of C libraries
Usage:
nimgen [options] <file.cfg>...
Options:
-f delete all artifacts and regenerate
"""
let ARGS = docopt(DOC)
# ###
# Helpers
proc execProc(cmd: string): string =
var p = startProcess(cmd, options = {poStdErrToStdOut, poUsePath, poEvalCommand})
result = ""
var outp = outputStream(p)
var line = newStringOfCap(120).TaintedString
while true:
if outp.readLine(line):
result.add(line)
result.add("\n")
elif not running(p): break
var x = p.peekExitCode()
if x != 0:
echo "Command failed: " & $x
echo cmd
echo result
quit(1)
proc extractZip(zipfile: string) =
var cmd = "unzip -o $#"
if defined(Windows):
cmd = "powershell -nologo -noprofile -command \"& { Add-Type -A 'System.IO.Compression.FileSystem'; [IO.Compression.ZipFile]::ExtractToDirectory('$#', '.'); }\""
setCurrentDir(OUTPUT)
defer: setCurrentDir("..")
echo "Extracting " & zipfile
discard execProc(cmd % zipfile)
proc downloadUrl(url: string) =
let file = url.extractFilename()
let ext = file.splitFile().ext.toLowerAscii()
var cmd = "curl $# -o $#"
if defined(Windows):
cmd = "powershell wget $# -OutFile $#"
if not (ext == ".zip" and fileExists(OUTPUT/file)):
echo "Downloading " & file
createDir(OUTPUT)
discard execProc(cmd % [url, OUTPUT/file])
if ext == ".zip":
extractZip(file)
proc gitReset() =
echo "Resetting Git repo"
setCurrentDir(OUTPUT)
defer: setCurrentDir("..")
discard execProc("git reset --hard HEAD")
proc gitRemotePull(url: string, pull=true) =
if dirExists(OUTPUT):
if pull:
gitReset()
return
createDir(OUTPUT)
setCurrentDir(OUTPUT)
defer: setCurrentDir("..")
echo "Setting up Git repo"
discard execProc("git init .")
discard execProc("git remote add origin " & url)
if pull:
echo "Checking out artifacts"
discard execProc("git pull --depth=1 origin master")
proc gitSparseCheckout(plist: string) =
let sparsefile = ".git/info/sparse-checkout"
if fileExists(OUTPUT/sparsefile):
gitReset()
return
setCurrentDir(OUTPUT)
defer: setCurrentDir("..")
discard execProc("git config core.sparsecheckout true")
writeFile(sparsefile, plist)
echo "Checking out artifacts"
discard execProc("git pull --depth=1 origin master")
proc getKey(ukey: string): tuple[key: string, val: bool] =
var kv = ukey.replace(re"\..*", "").split("-", 1)
if kv.len() == 1:
kv.add("")
if (kv[1] == "") or
(kv[1] == "win" and defined(Windows)) or
(kv[1] == "lin" and defined(Linux)) or
(kv[1] == "osx" and defined(MacOSX)):
return (kv[0], true)
return (kv[0], false)
# ###
# File loction
proc getnimout(file: string): string =
var nimout = file.splitFile().name.replace(re"[\-\.]", "_") & ".nim"
if OUTPUT != "":
nimout = OUTPUT/nimout
return nimout
proc exclude(file: string): bool =
for excl in EXCLUDES:
if excl in file:
return true
return false
proc search(file: string): string =
if exclude(file):
return ""
result = file
if file.splitFile().ext == ".nim":
result = getnimout(file)
elif not fileExists(result):
var found = false
for inc in INCLUDES:
result = inc/file
if fileExists(result):
found = true
break
if not found:
echo "File doesn't exist: " & file
quit(1)
return result.replace(re"[\\/]", $DirSep)
# ###
# Loading / unloading
template withFile(file: string, body: untyped): untyped =
if fileExists(file):
var f: File
while true:
try:
f = open(file)
break
except:
sleep(100)
var contentOrig = f.readAll()
f.close()
var content {.inject.} = contentOrig
body
if content != contentOrig:
var f = open(file, fmWrite)
write(f, content)
f.close()
else:
echo "Missing file " & file
# ###
# Manipulating content
proc prepend(file: string, data: string, search="") =
withFile(file):
if search == "":
content = data & content
else:
let idx = content.find(search)
if idx != -1:
content = content[0..<idx] & data & content[idx..<content.len()]
proc append(file: string, data: string, search="") =
withFile(file):
if search == "":
content &= data
else:
let idx = content.find(search)
let idy = idx + search.len()
if idx != -1:
content = content[0..<idy] & data & content[idy..<content.len()]
proc freplace(file: string, pattern: string, repl="") =
withFile(file):
if pattern in content:
content = content.replace(pattern, repl)
proc freplace(file: string, pattern: Regex, repl="") =
withFile(file):
if content.find(pattern).isSome():
if "$#" in repl:
for m in content.findIter(pattern):
content = content.replace(m.match, repl % m.captures[0])
else:
content = content.replace(pattern, repl)
proc compile(dir="", file=""): string =
proc fcompile(file: string): string =
return "{.compile: \"$#\".}" % file.replace("\\", "/")
var data = ""
if dir != "" and dirExists(dir):
for f in walkFiles(dir / "*.c"):
data &= fcompile(f) & "\n"
if file != "" and fileExists(file):
data &= fcompile(file) & "\n"
return data
proc fixfuncprotos(file: string) =
withFile(file):
for fp in content.findIter(re"(?m)(^.*?)[ ]*\(\*(.*?)\((.*?)\)\)[ \r\n]*\((.*?[\r\n]*.*?)\);"):
var tdout = "typedef $# (*type_$#)($#);\n" % [fp.captures[0], fp.captures[1], fp.captures[3]] &
"type_$# $#($#);" % [fp.captures[1], fp.captures[1], fp.captures[2]]
content = content.replace(fp.match, tdout)
# ###
# Convert to Nim
proc getincls(file: string): seq[string] =
result = @[]
withFile(file):
for f in content.findIter(re"(?m)^\s*#\s*include\s+(.*?)$"):
var inc = f.captures[0].strip()
if ((QUOTES and inc.contains("\"")) or (FILTER != "" and FILTER in inc)) and (not exclude(inc)):
result.add(inc.replace(re"""[<>"]""", "").strip())
result = result.deduplicate()
proc getdefines(file: string): string =
withFile(file):
result = ""
for def in content.findIter(re"(?m)^(\s*#\s*define\s+[\w\d_]+\s+[\d.x]+)(?:\r|//|/*).*?$"):
result &= def.captures[0] & "\n"
proc preprocess(file, ppflags, flags: string): string =
var pproc = "gcc"
if flags.contains("cpp"):
pproc = "g++"
var cmd = "$# -E $# $#" % [pproc, ppflags, file]
for inc in INCLUDES:
cmd &= " -I " & inc
# Run preprocessor
var data = execProc(cmd)
# Include content only from file
var rdata: Rope
var start = false
let sfile = file.replace("\\", "/")
for line in data.splitLines():
if line.strip() != "":
if line[0] == '#' and not line.contains("#pragma"):
start = false
if sfile in line.replace("\\", "/").replace("//", "/"):
start = true
else:
if start:
rdata.add(
line.replace("_Noreturn", "")
.replace("WINAPI", "")
.replace("__attribute__", "")
.replace(re"\(\([_a-z]+?\)\)", "")
.replace(re"\(\(__format__\(__printf__, \d, \d\)\)\);", ";") & "\n"
)
return $rdata
proc ctags(file: string): string =
var cmd = "ctags -o - --fields=+S+K --c-kinds=p --file-scope=no " & file
var fps = execProc(cmd)
var fdata = ""
for line in fps.splitLines():
var spl = line.split(re"\t")
if spl.len() > 4:
if spl[0] != "main":
var fn = ""
var match = spl[2].find(re"/\^(.*?)\(")
if match.isSome():
fn = match.get().captures[0]
fn &= spl[4].replace("signature:", "") & ";"
fdata &= fn & "\n"
return fdata
proc c2nim(fl, outfile, flags, ppflags: string, recurse, preproc, ctag, define: bool, dynlib, compile: seq[string] = @[]) =
var file = search(fl)
if file == "":
return
if file in DONE:
return
echo "Processing " & file
DONE.add(file)
fixfuncprotos(file)
var incout = ""
if recurse:
var incls = getincls(file)
for inc in incls:
incout &= "import " & inc.splitFile().name.replace(re"[\-\.]", "_") & "\n"
c2nim(inc, getnimout(inc), flags, ppflags, recurse, preproc, ctag, define, dynlib)
var cfile = file
if preproc:
cfile = "temp-$#.c" % [outfile.extractFilename()]
writeFile(cfile, preprocess(file, ppflags, flags))
elif ctag:
cfile = "temp-$#.c" % [outfile.extractFilename()]
writeFile(cfile, ctags(file))
if define and (preproc or ctag):
prepend(cfile, getdefines(file))
var extflags = ""
var passC = ""
var outlib = ""
passC = "import strutils\n"
for inc in INCLUDES:
passC &= ("""{.passC: "-I\"" & gorge("nimble path $#").strip() & "/$#\"".}""" % [OUTPUT, inc]) & "\n"
let fname = file.splitFile().name
if dynlib.len() != 0:
let win = "when defined(Windows):\n"
let lin = "when defined(Linux):\n"
let osx = "when defined(MacOSX):\n"
var winlib, linlib, osxlib: string = ""
for dl in dynlib:
let lib = " const dynlib$# = \"$#\"\n" % [fname, dl]
let ext = dl.splitFile().ext
if ext == ".dll":
winlib &= lib
elif ext == ".so":
linlib &= lib
elif ext == ".dylib":
osxlib &= lib
if winlib != "":
outlib &= win & winlib & "\n"
if linlib != "":
outlib &= lin & linlib & "\n"
if osxlib != "":
outlib &= osx & osxlib & "\n"
if outlib != "":
extflags &= " --dynlib:dynlib$#" % fname
else:
passC &= "const header$# = \"$#\"\n" % [fname, fl]
extflags = "--header:header$#" % fname
# Run c2nim on generated file
var cmd = "c2nim $# $# --out:$# $#" % [flags, extflags, outfile, cfile]
when defined(windows):
cmd = "cmd /c " & cmd
discard execProc(cmd)
if preproc or ctag:
try:
removeFile(cfile)
except:
discard
# Import nim modules
if recurse:
prepend(outfile, incout)
# Nim doesn't like {.cdecl.} for type proc()
freplace(outfile, re"(?m)(.*? = proc.*?){.cdecl.}", "$#")
freplace(outfile, " {.cdecl.})", ")")
# Include {.compile.} directives
for cpl in compile:
if getFileInfo(cpl).kind == pcFile:
prepend(outfile, compile(file=cpl))
else:
prepend(outfile, compile(dir=cpl))
# Add header file and include paths
if passC != "":
prepend(outfile, passC)
# Add dynamic library
if outlib != "":
prepend(outfile, outlib)
# ###
# Processor
proc runcfg(cfg: string) =
if not fileExists(cfg):
echo "Config doesn't exist: " & cfg
quit(1)
CONFIG = loadConfig(cfg)
if CONFIG.hasKey("n.global"):
if CONFIG["n.global"].hasKey("output"):
OUTPUT = CONFIG["n.global"]["output"]
if ARGS["-f"]:
removeDir(OUTPUT)
if CONFIG["n.global"].hasKey("filter"):
FILTER = CONFIG["n.global"]["filter"]
if CONFIG["n.global"].hasKey("quotes"):
if CONFIG["n.global"]["quotes"] == "false":
QUOTES = false
if CONFIG.hasKey("n.include"):
for inc in CONFIG["n.include"].keys():
INCLUDES.add(inc)
if CONFIG.hasKey("n.exclude"):
for excl in CONFIG["n.exclude"].keys():
EXCLUDES.add(excl)
if CONFIG.hasKey("n.prepare"):
for prep in CONFIG["n.prepare"].keys():
let (key, val) = getKey(prep)
if val == true:
if key == "download":
downloadUrl(CONFIG["n.prepare"][prep])
elif key == "git":
gitRemotePull(CONFIG["n.prepare"][prep])
elif key == "gitremote":
gitRemotePull(CONFIG["n.prepare"][prep], false)
elif key == "gitsparse":
gitSparseCheckout(CONFIG["n.prepare"][prep])
elif key == "execute":
discard execProc(CONFIG["n.prepare"][prep])
for file in CONFIG.keys():
if file in @["n.global", "n.include", "n.exclude", "n.prepare"]:
continue
var sfile = search(file)
var srch = ""
var compile: seq[string] = @[]
var dynlib: seq[string] = @[]
for act in CONFIG[file].keys():
let (action, val) = getKey(act)
if val == true:
if action == "create":
createDir(file.splitPath().head)
writeFile(file, CONFIG[file][act])
elif action in @["prepend", "append", "replace", "compile", "dynlib"] and sfile != "":
if action == "prepend":
if srch != "":
prepend(sfile, CONFIG[file][act], CONFIG[file][srch])
else:
prepend(sfile, CONFIG[file][act])
elif action == "append":
if srch != "":
append(sfile, CONFIG[file][act], CONFIG[file][srch])
else:
append(sfile, CONFIG[file][act])
elif action == "replace":
if srch != "":
freplace(sfile, CONFIG[file][srch], CONFIG[file][act])
elif action == "compile":
compile.add(CONFIG[file][act])
elif action == "dynlib":
dynlib.add(CONFIG[file][act])
srch = ""
elif action == "search":
srch = act
if file.splitFile().ext != ".nim":
var recurse = false
var preproc = false
var ctag = false
var define = false
var noprocess = false
var flags = "--stdcall"
var ppflags = ""
for act in CONFIG[file].keys():
if CONFIG[file][act] == "true":
if act == "recurse":
recurse = true
elif act == "preprocess":
preproc = true
elif act == "ctags":
ctag = true
elif act == "defines":
define = true
elif act == "noprocess":
noprocess = true
elif act == "flags":
flags = CONFIG[file][act]
elif act == "ppflags":
ppflags = CONFIG[file][act]
if not noprocess:
c2nim(file, getnimout(file), flags, ppflags, recurse, preproc, ctag, define, dynlib, compile)
# ###
# Main loop
for i in ARGS["<file.cfg>"]:
runcfg(i)

View file

@ -1,17 +1,18 @@
# Package # Package
version = "0.1.1" version = "0.5.1"
author = "genotrance" author = "genotrance"
description = "c2nim helper to simpilfy and automate the wrapping of C libraries" description = "c2nim helper to simplify and automate the wrapping of C libraries"
license = "MIT" license = "MIT"
skipDirs = @["tests"] bin = @["nimgen"]
srcDir = "src"
skipDirs = @["nimgen", "tests", "web"]
# Dependencies # Dependencies
requires "nim >= 0.16.0", "c2nim >= 0.9.13", "docopt >= 0.6.5" requires "nim >= 0.19.0", "c2nim >= 0.9.14", "regex >= 0.10.0"
bin = @["nimgen"]
task test, "Test nimgen": task test, "Test nimgen":
exec "nim c -r tests/rununittests.nim"
exec "nim e tests/nimgentest.nims" exec "nim e tests/nimgentest.nims"

5
src/nimgen.nim Normal file
View file

@ -0,0 +1,5 @@
import os
import nimgen/runcfg
runCli()

131
src/nimgen/c2nim.nim Normal file
View file

@ -0,0 +1,131 @@
import os, regex, strutils
when (NimMajor, NimMinor, NimPatch) < (0, 19, 9):
import ospaths
import external, file, fileops, gencore, globals
proc relativePath(path: string): string =
if gOutput.len() == 0:
result = path
else:
# multiReplace() bug - #9557
result = path.replace(gOutput, "")
return result.multiReplace([("\\", "/"), ("//", "/")])
proc c2nim*(fl, outfile: string, c2nimConfig: c2nimConfigObj) =
var file = search(fl)
if file.len() == 0:
return
echo "Generating " & outfile
var cfile = file
if c2nimConfig.preprocess:
cfile = "temp-$#.c" % [outfile.extractFilename()]
writeFileFlush(cfile, runPreprocess(file, c2nimConfig.ppflags, c2nimConfig.flags, c2nimConfig.inline))
elif c2nimConfig.ctags:
cfile = "temp-$#.c" % [outfile.extractFilename()]
writeFileFlush(cfile, runCtags(file))
if c2nimConfig.removeBodies:
removeBodies(cfile)
if c2nimConfig.defines and (c2nimConfig.preprocess or c2nimConfig.ctags):
prepend(cfile, getDefines(file, c2nimConfig.inline))
removeStatic(cfile)
var
extflags = ""
passC = ""
outlib = ""
outpragma = ""
passC = "import strutils\n"
passC &= """const sourcePath = currentSourcePath().split({'\\', '/'})[0..^2].join("/")""" & "\n"
for inc in gIncludes:
if inc.isAbsolute():
passC &= ("""{.passC: "-I\"$#\"".}""" % [inc.sanitizePath()]) & "\n"
else:
passC &= (
"""{.passC: "-I\"" & sourcePath & "$#\"".}""" %
inc.relativePath()
) & "\n"
for prag in c2nimConfig.pragma:
outpragma &= "{." & prag & ".}\n"
let fname = file.splitFile().name.multiReplace([(".", "_"), ("-", "_")])
if c2nimConfig.dynlib.len() != 0:
let
win = "when defined(Windows):\n"
lin = "when defined(Linux):\n"
osx = "when defined(MacOSX):\n"
var winlib, linlib, osxlib: string = ""
for dl in c2nimConfig.dynlib:
let
lib = " const dynlib$# = \"$#\"\n" % [fname, dl]
ext = dl.splitFile().ext
if ext == ".dll":
winlib &= lib
elif ext == ".so":
linlib &= lib
elif ext == ".dylib":
osxlib &= lib
if winlib != "":
outlib &= win & winlib & "\n"
if linlib != "":
outlib &= lin & linlib & "\n"
if osxlib != "":
outlib &= osx & osxlib & "\n"
if outlib != "":
extflags &= " --dynlib:dynlib$#" % fname
else:
if file.isAbsolute():
passC &= "const header$# = \"$#\"\n" % [fname, file]
else:
passC &= "const header$# = sourcePath & \"$#\"\n" %
[fname, file.relativePath()]
extflags = "--header:header$#" % fname
# Run c2nim on generated file
var cmd = "c2nim $# $# --out:$# $#" % [c2nimConfig.flags, extflags, outfile, cfile]
when defined(windows):
cmd = "cmd /c " & cmd.quoteShell
discard execProc(cmd)
if c2nimConfig.preprocess or c2nimConfig.ctags:
try:
removeFile(cfile)
except:
discard
else:
if c2nimConfig.removeBodies:
reAddBodies(cfile)
reAddStatic(cfile)
# Nim doesn't like {.cdecl.} for type proc()
freplace(outfile, re"(?m)(.*? = proc.*?)\{.cdecl.\}", "$1")
freplace(outfile, " {.cdecl.})", ")")
# Include {.compile.} directives
for cpl in c2nimConfig.compile:
prepend(outfile, compile(cpl, c2nimConfig.flags))
# Add any pragmas
if outpragma != "":
prepend(outfile, outpragma)
# Add header file and include paths
if passC != "":
prepend(outfile, passC)
# Add dynamic library
if outlib != "":
prepend(outfile, outlib)

174
src/nimgen/external.nim Normal file
View file

@ -0,0 +1,174 @@
import os, osproc, regex, streams, strutils
import globals
proc sanitizePath*(path: string): string =
path.multiReplace([("\\", "/"), ("//", "/")])
proc execProc*(cmd: string): string =
var ret: int
(result, ret) = execCmdEx(cmd)
if ret != 0:
echo "Command failed: " & $ret
echo cmd
echo result
quit(1)
proc execAction*(cmd: string): string =
var ccmd = ""
when defined(Windows):
ccmd = "cmd /c " & cmd.replace("/", "\\")
when defined(Linux) or defined(MacOSX):
ccmd = "bash -c '" & cmd & "'"
echo "Running '" & ccmd[0..<min(64, len(ccmd))] & "'"
return execProc(ccmd)
proc extractZip*(zipfile: string) =
var cmd = "unzip -o $#"
if defined(Windows):
cmd = "powershell -nologo -noprofile -command \"& { Add-Type -A " &
"'System.IO.Compression.FileSystem'; " &
"[IO.Compression.ZipFile]::ExtractToDirectory('$#', '.'); }\""
setCurrentDir(gOutput)
defer: setCurrentDir(gProjectDir)
echo "Extracting " & zipfile
discard execProc(cmd % zipfile)
proc downloadUrl*(url: string) =
let
file = url.extractFilename()
ext = file.splitFile().ext.toLowerAscii()
var cmd = if defined(Windows):
"powershell [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; wget $# -OutFile $#"
else:
"curl -L $# -o $#"
if not (ext == ".zip" and fileExists(gOutput/file)):
echo "Downloading " & file
discard execProc(cmd % [url, gOutput/file])
if ext == ".zip":
extractZip(file)
template setGitDir() =
setCurrentDir(gGitOutput)
defer: setCurrentDir(gProjectDir)
proc gitReset*() =
echo "Resetting " & gGitOutput
setGitDir()
let cmd = "git reset --hard"
while execCmdEx(cmd)[0].contains("Permission denied"):
sleep(1000)
echo " Retrying ..."
proc gitCheckout*(file: string) =
echo "Resetting " & file
setGitDir()
let cmd = "git checkout $#" % file.replace(gGitOutput & "/", "")
while execCmdEx(cmd)[0].contains("Permission denied"):
sleep(500)
echo " Retrying ..."
proc gitPull() =
if gGitCheckout.len() != 0:
echo "Checking out " & gGitCheckout
discard execProc("git pull --tags origin master")
discard execProc("git checkout " & gGitCheckout)
gGitCheckout = ""
else:
echo "Pulling repository"
discard execProc("git pull --depth=1 origin master")
proc gitRemotePull*(url: string, pull=true) =
if dirExists(gGitOutput/".git"):
if pull:
gitReset()
return
setGitDir()
echo "Setting up Git repo: " & url
discard execProc("git init .")
discard execProc("git remote add origin " & url)
if pull:
gitPull()
proc gitSparseCheckout*(plist: string) =
let sparsefile = ".git/info/sparse-checkout"
if fileExists(gGitOutput/sparsefile):
gitReset()
return
setGitDir()
discard execProc("git config core.sparsecheckout true")
writeFile(sparsefile, plist)
gitPull()
proc runPreprocess*(file, ppflags, flags: string, inline: bool): string =
var
pproc = if flags.contains("cpp"): gCppCompiler else: gCCompiler
cmd = "$# -E $# $#" % [pproc, ppflags, file]
for inc in gIncludes:
cmd &= " -I " & inc.quoteShell
# Run preprocessor
var data = execProc(cmd)
# Include content only from file
var
rdata: seq[string] = @[]
start = false
sfile = file.sanitizePath
if inline:
sfile = sfile.parentDir()
for line in data.splitLines():
if line.strip() != "":
if line[0] == '#' and not line.contains("#pragma"):
start = false
if sfile in line.sanitizePath:
start = true
if not ("\\" in line) and not ("/" in line) and extractFilename(sfile) in line:
start = true
else:
if start:
rdata.add(
line.multiReplace([("_Noreturn", ""), ("(())", ""), ("WINAPI", ""),
("__attribute__", ""), ("extern \"C\"", "")])
.replace(re"\(\([_a-z]+?\)\)", "")
.replace(re"\(\(__format__[\s]*\(__[gnu_]*printf__, [\d]+, [\d]+\)\)\);", ";")
)
return rdata.join("\n")
proc runCtags*(file: string): string =
var
cmd = "ctags -o - --fields=+S+K --c-kinds=+p --file-scope=no " & file
fps = execProc(cmd)
fdata = ""
for line in fps.splitLines():
var spl = line.split('\t')
if spl.len() > 4:
if spl[0] != "main" and spl[3] != "member":
var fn = ""
var match: RegexMatch
if spl[2].find(re"/\^(.*?)\(", match):
fn = spl[2][match.group(0)[0]]
fn &= spl[4].replace("signature:", "") & ";"
fdata &= fn & "\n"
return fdata

120
src/nimgen/file.nim Normal file
View file

@ -0,0 +1,120 @@
import os, pegs, regex, strutils, tables
when (NimMajor, NimMinor, NimPatch) < (0, 19, 9):
import ospaths
import globals, external
# ###
# File loction
proc getNimout*(file: string, rename=true): string =
result = file.splitFile().name.multiReplace([("-", "_"), (".", "_")]) & ".nim"
if gOutput != "":
result = gOutput & "/" & result
if not rename:
return
if gRenames.hasKey(file):
result = gRenames[file]
createDir(parentDir(result))
proc exclude*(file: string): bool =
for excl in gExcludes:
if excl in file:
return true
return false
proc search*(file: string): string =
if exclude(file):
return ""
result = file
if file.splitFile().ext == ".nim":
result = getNimout(file)
elif not fileExists(result) and not dirExists(result):
var found = false
for inc in gIncludes:
result = inc & "/" & file
if fileExists(result) or dirExists(result):
found = true
break
if not found:
echo "File doesn't exist: " & file
quit(1)
# Only keep relative directory
return result.sanitizePath.replace(gProjectDir & "/", "")
proc rename*(file: string, renfile: string) =
if file.splitFile().ext == ".nim":
return
var
nimout = getNimout(file, false)
newname = renfile.replace("$nimout", extractFilename(nimout))
if newname =~ peg"(!\$.)*{'$replace'\s*'('\s*{(!\)\S)+}')'}":
var final = nimout.extractFilename()
for entry in matches[1].split(","):
let spl = entry.split("=")
if spl.len() != 2:
echo "Bad replace syntax: " & renfile
quit(1)
let
srch = spl[0].strip()
repl = spl[1].strip()
final = final.replace(srch, repl)
newname = newname.replace(matches[0], final)
gRenames[file] = gOutput & "/" & newname
# ###
# Actions
proc openRetry*(file: string, mode: FileMode = fmRead): File =
while true:
try:
result = open(file, mode)
break
except IOError:
sleep(100)
template writeFileFlush*(file, content: string): untyped =
let f = openRetry(file, fmWrite)
f.write(content)
f.flushFile()
f.close()
template withFile*(file: string, body: untyped): untyped =
if fileExists(file):
var f = openRetry(file)
var contentOrig = f.readAll()
f.close()
var content {.inject.} = contentOrig
body
if content != contentOrig:
writeFileFlush(file, content)
else:
echo "Missing file " & file
proc doCopy*(flist: string) =
for pair in flist.split(","):
let spl = pair.split("=")
if spl.len() != 2:
echo "Bad copy syntax: " & flist
quit(1)
let
lfile = spl[0].strip()
rfile = spl[1].strip()
copyFile(lfile, rfile)
echo "Copied $# to $#" % [lfile, rfile]

145
src/nimgen/fileops.nim Normal file
View file

@ -0,0 +1,145 @@
import os, regex, strutils
import external, file
# ###
# Manipulating content
proc prepend*(file: string, data: string, search="") =
withFile(file):
if search == "":
content = data & content
else:
let idx = content.find(search)
if idx != -1:
content = content[0..<idx] & data & content[idx..<content.len()]
proc pipe*(file: string, command: string) =
let cmd = command % ["file", file]
let commandResult = execProc(cmd).strip()
if commandResult != "":
withFile(file):
content = commandResult
proc append*(file: string, data: string, search="") =
withFile(file):
if search == "":
content &= data
else:
let idx = content.find(search)
let idy = idx + search.len()
if idx != -1:
content = content[0..<idy] & data & content[idy..<content.len()]
proc freplace*(file: string, pattern: string|Regex, repl="") =
withFile(file):
content = content.replace(pattern, repl)
proc move*(file: string, pattern: string|Regex, move: string) =
var tomove: seq[string] = @[]
withFile(file):
when pattern is string:
tomove.add(pattern)
when pattern is Regex:
var ms = content.findAll(pattern)
for i, m in ms:
tomove.add(content[m.group(0)[0]])
content = content.replace(pattern, "")
for i in tomove:
append(file, i, move)
proc comment*(file: string, pattern: string, numlines: string) =
let
ext = file.splitFile().ext.toLowerAscii()
cmtchar = if ext == ".nim": "#" else: "//"
withFile(file):
var
idx = content.find(pattern)
num = 0
try:
num = numlines.parseInt()
except ValueError:
echo "Bad comment value, should be integer: " & numlines
if idx != -1:
for i in 0 .. num-1:
if idx >= content.len():
break
content = content[0..<idx] & cmtchar & content[idx..<content.len()]
while idx < content.len():
idx += 1
if content[idx] == '\L':
idx += 1
break
proc removeStatic*(filename: string) =
## Replace static function bodies with a semicolon and commented
## out body
withFile(filename):
content = content.replace(
re"(?m)(static inline.*?\))(\s*\{(\s*?.*?$)*?[\n\r]\})",
proc (m: RegexMatch, s: string): string =
let funcDecl = s[m.group(0)[0]]
let body = s[m.group(1)[0]].strip()
result = ""
result.add("$#;" % [funcDecl])
result.add(body.replace(re"(?m)^(.*\n?)", "//$1"))
)
proc removeBodies*(filename: string) =
## Replace function bodies with a semicolon and commented
## out body
withFile(filename):
content = content.replace(
re"(?m)(.*?\))(\s*\{(\s*?.*?$)*?[\n\r]\})",
proc (m: RegexMatch, s: string): string =
let funcDecl = s[m.group(0)[0]]
let body = s[m.group(1)[0]].strip()
result = ""
result.add("$#;" % [funcDecl])
result.add(body.replace(re"(?m)^(.*\n?)", "//$1"))
)
proc reAddBodies*(filename: string) =
## Uncomment out the body and remove the semicolon. Undoes
## removeBodies
withFile(filename):
content = content.replace(
re"(?m)(.*?\))(\s*\{(\s*?.*?$)*?[\n\r]\})",
proc (m: RegexMatch, s: string): string =
let funcDecl = s[m.group(0)[0]]
let body = s[m.group(1)[0]].strip()
result = ""
result.add("$# " % [funcDecl])
result.add(body.replace(re"(?m)^\/\/(.*\n?)", "$1"))
)
proc reAddStatic*(filename: string) =
## Uncomment out the body and remove the semicolon. Undoes
## removeStatic
withFile(filename):
content = content.replace(
re"(?m)(static inline.*?\));(\/\/\s*\{(\s*?.*?$)*?[\n\r]\/\/\})",
proc (m: RegexMatch, s: string): string =
let funcDecl = s[m.group(0)[0]]
let body = s[m.group(1)[0]].strip()
result = ""
result.add("$# " % [funcDecl])
result.add(body.replace(re"(?m)^\/\/(.*\n?)", "$1"))
)
proc fixFuncProtos*(file: string) =
withFile(file):
content = content.replace(re"(?m)(^.*?)[ ]*\(\*(.*?)\((.*?)\)\)[ \r\n]*\((.*?[\r\n]*.*?)\);",
proc (m: RegexMatch, s: string): string =
(("typedef $# (*type_$#)($#);\n" % [s[m.group(0)[0]], s[m.group(1)[0]], s[m.group(3)[0]]]) &
("type_$# $#($#);" % [s[m.group(1)[0]], s[m.group(1)[0]], s[m.group(2)[0]]]))
)

108
src/nimgen/gencore.nim Normal file
View file

@ -0,0 +1,108 @@
import os, regex, sequtils, strutils
import file, globals
proc addEnv*(str: string): string =
var newStr = str
if "$output" in newStr or "${output}" in newStr:
newStr = newStr % ["output", gOutput]
for pair in envPairs():
if pair.key.len() == 0:
continue
if ("$" & pair.key) in newStr or ("${" & pair.key & "}") in newStr:
newStr = newStr % [pair.key, pair.value.string]
return newStr
proc compile*(cpl, flags: string): string =
var data = ""
proc fcompile(file: string): string =
let fn = file.splitFile().name
var
ufn = fn
uniq = 1
while ufn in gCompile:
ufn = fn & $uniq
uniq += 1
gCompile.add(ufn)
if fn == ufn:
return "{.compile: \"$#\".}" % file.replace("\\", "/")
else:
return "{.compile: (\"../$#\", \"$#.o\").}" % [file.replace("\\", "/"), ufn]
proc dcompile(dir: string) =
for f in walkFiles(dir):
data &= fcompile(f) & "\n"
if cpl.contains("*") or cpl.contains("?"):
dcompile(cpl)
else:
let fcpl = search(cpl)
if getFileInfo(fcpl).kind == pcFile:
data &= fcompile(fcpl) & "\n"
elif getFileInfo(fcpl).kind == pcDir:
if flags.contains("cpp"):
for i in @["*.C", "*.cpp", "*.c++", "*.cc", "*.cxx"]:
dcompile(fcpl / i)
else:
dcompile(fcpl / "*.c")
return data
proc getIncls*(file: string, inline=false): seq[string] =
result = @[]
if inline and file in gDoneInline:
return
let curPath = splitFile(expandFileName(file)).dir
withFile(file):
for f in content.findAll(re"(?m)^\s*#\s*include\s+(.*?)$"):
var inc = content[f.group(0)[0]].strip()
if ((gQuotes and inc.contains("\"")) or (gFilter != "" and gFilter in inc)) and (not exclude(inc)):
let addInc = inc.replace(re"""[<>"]""", "").replace(re"\/[\*\/].*$", "").strip()
try:
# Try searching for a local library. expandFilename will throw
# OSError if the file does not exist
let
finc = expandFileName(curPath / addInc)
fname = finc.replace(curPath & $DirSep, "")
if fname.len() > 0:
# only add if the file is non-empty
result.add(fname.search())
except OSError:
# If it's a system library
result.add(addInc)
result = result.deduplicate()
gDoneInline.add(file)
if inline:
var sres = newSeq[string]()
for incl in result:
let sincl = search(incl)
if sincl == "":
continue
sres.add(getIncls(sincl, inline))
result.add(sres)
result = result.deduplicate()
proc getDefines*(file: string, inline=false): string =
result = ""
if inline:
var incls = getIncls(file, inline)
for incl in incls:
let sincl = search(incl)
if sincl != "":
result &= getDefines(sincl)
withFile(file):
for def in content.findAll(re"(?m)^(\s*#\s*define\s+[\w\d_]+\s+[\d\-.xf]+)(?:\r|//|/*).*?$"):
result &= content[def.group(0)[0]] & "\n"

47
src/nimgen/globals.nim Normal file
View file

@ -0,0 +1,47 @@
import os, parsecfg, tables
const
cCompilerEnv* = "CC"
cppCompilerEnv* = "CPP"
defaultCCompiler* = "gcc"
defaultCppCompiler* = "g++"
var
# Config
gConfig*: Config
gExcludes*: seq[string] = @[]
gIncludes*: seq[string] = @[]
gRenames* = initTable[string, string]()
gWildcards* = newConfig()
# n.global
gOutput* = "."
gQuotes* = true
gFilter* = ""
gCppCompiler* = getEnv(cppCompilerEnv, defaultCCompiler)
gCCompiler* = getEnv(cCompilerEnv, defaultCppCompiler)
# State tracking
gGitCheckout* = ""
gGitOutput* = ""
gProjectDir* = ""
gCompile*: seq[string] = @[]
gDoneInline*: seq[string] = @[]
gDoneRecursive*: seq[string] = @[]
type
c2nimConfigObj* = object
flags*, ppflags*: string
recurse*, inline*, preprocess*, removeBodies*, ctags*, defines*: bool
dynlib*, compile*, pragma*: seq[string]
const gDoc* = """
Nimgen is a helper for c2nim to simpilfy and automate the wrapping of C libraries
Usage:
nimgen [options] <file.cfg>...
Options:
-f delete all artifacts and regenerate
"""

411
src/nimgen/runcfg.nim Normal file
View file

@ -0,0 +1,411 @@
import os, parsecfg, regex, strutils, tables
import c2nim, external, file, fileops, gencore, globals
proc `[]`*(table: OrderedTableRef[string, string], key: string): string =
## Gets table values with env vars inserted
tables.`[]`(table, key).addEnv
proc getKey(ukey: string, section = false): tuple[key: string, val: bool] =
var kv = if not section: ukey.replace(re"\..*", "").split("-", 1) else: ukey.split("-", 1)
if kv.len() == 1:
kv.add("")
if kv[1] == "":
return (kv[0], true)
for ostyp in kv[1].split("-"):
if (ostyp == "win" and defined(Windows)) or
(ostyp == "lin" and defined(Linux)) or
((ostyp == "osx" or ostyp == "mac") and defined(MacOSX)) or
(ostyp == "unix" and (defined(Linux) or defined(MacOSX))):
return (kv[0], true)
return (kv[0], false)
proc runFile*(file: string, cfgin: OrderedTableRef = newOrderedTable[string, string]()) =
var
cfg = cfgin
sfile = search(file)
nowildcard = false
if sfile in gDoneRecursive:
return
if sfile.len() != 0:
echo "Processing " & sfile
gDoneRecursive.add(sfile)
for act in cfg.keys():
let (action, val) = getKey(act)
if val == true and action == "nowildcard" and cfg[act] == "true":
nowildcard = true
break
if not nowildcard:
for pattern in gWildcards.keys():
var m: RegexMatch
let pat = pattern.replace(".", "\\.").replace("*", ".*").replace("?", ".?")
if file.find(toPattern(pat), m):
echo " Appending keys for wildcard " & pattern
for key in gWildcards[pattern].keys():
cfg[key & "." & pattern] = gWildcards[pattern][key]
var
srch = ""
rgx = ""
c2nimConfig = c2nimConfigObj(
flags: "--stdcall", ppflags: "",
recurse: false, inline: false, preprocess: false, ctags: false, defines: false,
dynlib: @[], compile: @[], pragma: @[]
)
for act in cfg.keys():
let (action, val) = getKey(act)
if val == true:
if action == "create":
echo "Creating " & file
createDir(file.splitPath().head)
writeFileFlush(file, cfg[act])
if file in gExcludes:
gExcludes.delete(gExcludes.find(file))
sfile = file
gDoneRecursive.add(sfile)
elif action in @["prepend", "append", "replace", "move", "comment",
"rename", "compile", "dynlib", "pragma", "pipe"] and
sfile != "":
if action == "prepend":
if srch != "":
prepend(sfile, cfg[act], cfg[srch])
else:
prepend(sfile, cfg[act])
elif action == "append":
if srch != "":
append(sfile, cfg[act], cfg[srch])
else:
append(sfile, cfg[act])
elif action == "replace":
if srch != "":
freplace(sfile, cfg[srch], cfg[act])
elif rgx != "":
freplace(sfile, toPattern(cfg[rgx]), cfg[act])
elif action == "move":
if srch != "":
move(sfile, cfg[srch], cfg[act])
elif rgx != "":
move(sfile, toPattern(cfg[rgx]), cfg[act])
elif action == "comment":
if srch != "":
comment(sfile, cfg[srch], cfg[act])
elif action == "rename":
rename(sfile, cfg[act])
elif action == "compile":
c2nimConfig.compile.add(cfg[act])
elif action == "dynlib":
c2nimConfig.dynlib.add(cfg[act])
elif action == "pragma":
c2nimConfig.pragma.add(cfg[act])
elif action == "pipe":
pipe(sfile, cfg[act])
srch = ""
rgx = ""
elif action == "search":
srch = act
elif action == "regex":
rgx = act
if file.splitFile().ext != ".nim":
var
noprocess = false
reset = false
for act in cfg.keys():
let (action, val) = getKey(act)
if val == true:
if cfg[act] == "true":
if action == "recurse":
c2nimConfig.recurse = true
elif action == "inline":
c2nimConfig.inline = true
elif action == "preprocess":
c2nimConfig.preprocess = true
elif action == "removeBodies":
c2nimConfig.removeBodies = true
elif action == "ctags":
c2nimConfig.ctags = true
elif action == "defines":
c2nimConfig.defines = true
elif action == "noprocess":
noprocess = true
elif action == "reset":
reset = true
elif action == "flags":
c2nimConfig.flags = cfg[act]
elif action == "ppflags":
c2nimConfig.ppflags = cfg[act]
if c2nimConfig.recurse and c2nimConfig.inline:
echo "Cannot use recurse and inline simultaneously"
quit(1)
removeStatic(sfile)
fixFuncProtos(sfile)
let outfile = getNimout(sfile)
var incout = ""
if c2nimConfig.recurse or c2nimConfig.inline:
var
cfg = newOrderedTable[string, string]()
incls = getIncls(sfile)
for name, value in c2nimConfig.fieldPairs:
when value is string:
cfg[name] = value
when value is bool:
cfg[name] = $value
for i in c2nimConfig.dynlib:
cfg["dynlib." & i] = i
if c2nimConfig.inline:
cfg["noprocess"] = "true"
for inc in incls:
runFile(inc, cfg)
if c2nimConfig.recurse:
incout &= "import $#\n" % inc.search().getNimout()[0 .. ^5]
if not noprocess:
c2nim(file, outfile, c2nimConfig)
if c2nimConfig.recurse and incout.len() != 0:
prepend(outfile, incout)
if reset:
gitCheckout(sfile)
proc setOutputDir(dir: string) =
gOutput = dir.sanitizePath
if dirExists(gOutput):
if "-f" in commandLineParams():
try:
removeDir(gOutput)
except OSError:
echo "Directory in use: " & gOutput
quit(1)
else:
for f in walkFiles(gOutput/"*.nim"):
try:
removeFile(f)
except OSError:
echo "Unable to delete: " & f
quit(1)
createDir(gOutput)
gGitOutput = gOutput
proc runCfg*(cfg: string) =
if not fileExists(cfg):
echo "Config doesn't exist: " & cfg
quit(1)
gProjectDir = parentDir(cfg.expandFilename()).sanitizePath
gConfig = loadConfig(cfg)
gCppCompiler = getEnv(cppCompilerEnv, defaultCppCompiler).quoteShell
gCCompiler = getEnv(cCompilerEnv, defaultCCompiler).quoteShell
gGitOutput = gOutput
for section in gConfig.keys():
let (sname, sval) = getKey(section, true)
if not sval:
continue
case sname:
of "n.global":
for glob in gConfig[section].keys():
let (key, val) = getKey(glob)
if val == true:
let globVal = gConfig[section][glob]
case key:
of "output":
setOutputDir(globVal)
of "cpp_compiler":
gCppCompiler = globVal.quoteShell
of "c_compiler":
gCCompiler = globVal.quoteShell
of "filter":
gFilter = globVal
of "quotes":
if globVal == "false":
gQuotes = false
of "n.include":
for inc in gConfig[section].keys():
gIncludes.add(inc.addEnv().sanitizePath)
of "n.exclude":
for excl in gConfig[section].keys():
gExcludes.add(excl.addEnv().sanitizePath)
of "n.prepare":
for prep in gConfig[section].keys():
let (key, val) = getKey(prep)
if val == true:
let prepVal = gConfig[section][prep]
case key:
of "download":
downloadUrl(prepVal)
of "extract":
extractZip(prepVal)
of "gitcheckout":
gGitCheckout = prepVal
of "gitoutput":
gGitOutput = gOutput/prepVal
createDir(gGitOutput)
of "git":
gitRemotePull(prepVal)
of "gitremote":
gitRemotePull(prepVal, false)
of "gitsparse":
gitSparseCheckout(prepVal)
of "execute":
discard execAction(prepVal)
of "copy":
doCopy(prepVal)
of "n.wildcard":
var wildcard = ""
for wild in gConfig[section].keys():
let (key, val) = getKey(wild)
if val == true:
if key == "wildcard":
wildcard = gConfig[section][wild]
else:
gWildcards.setSectionKey(wildcard, wild,
gConfig[section][wild])
of "n.sourcefile":
for pattern in gConfig[section].keys():
for file in walkFiles(pattern.addEnv):
runFile(file)
of "n.post":
for post in gConfig[section].keys():
let (key, val) = getKey(post)
if val == true:
let postVal = gConfig[section][post]
case key:
of "gitoutput":
gGitOutput = gOutput/postVal
of "reset":
gitReset()
of "execute":
discard execAction(postVal)
else:
runFile(section, gConfig[section])
let gHelp = """
Nimgen is a helper for c2nim to simplify and automate the wrapping of C libraries
Usage:
nimgen [options] file.cfg|file.h ...
Params:
-C<compile> add compile entry *
-E<exclude> add n.exclude entry *
-F<flags> set c2nim flags *
-I<include> add n.include dir *
-O<outdir> set output directory
-P<ppflags> set preprocessor flags *
Options:
-c set ctags = true
-d set defines = true
-i set inline = true
-n set noprocess = true
-p set preprocess = true
-r set recurse = true
Editing:
-a<append> append string *
-e<prepend> prepend string *
-l<replace> replace string *
-o#lines comment X lines *
-s<search> search string *
-x<regex> regex search string *
* supports multiple instances
"""
proc runCli*() =
var
cfg = newOrderedTable[string, string]()
files: seq[string]
uniq = 1
gProjectDir = getCurrentDir().sanitizePath
for param in commandLineParams():
let flag = if param.len() <= 2: param else: param[0..<2]
if fileExists(param):
if param.splitFile().ext.toLowerAscii() == ".cfg":
runCfg(param)
else:
files.add(param)
elif flag == "-C":
cfg["compile." & $uniq] = param[2..^1]
elif flag == "-E":
gExcludes.add(param[2..^1].addEnv().sanitizePath)
elif flag == "-F":
if cfg.hasKey("flags"):
cfg["flags"] = cfg["flags"] & " " & param[2..^1]
else:
cfg["flags"] = param[2..^1]
elif flag == "-I":
gIncludes.add(param[2..^1].addEnv().sanitizePath)
elif flag == "-O":
setOutputDir(param[2..^1])
elif flag == "-P":
if cfg.hasKey("ppflags"):
cfg["ppflags"] = cfg["ppflags"] & " " & param[2..^1]
else:
cfg["ppflags"] = param[2..^1]
elif flag == "-c":
cfg["ctags"] = "true"
elif flag == "-d":
cfg["defines"] = "true"
elif flag == "-i":
cfg["inline"] = "true"
elif flag == "-n":
cfg["noprocess"] = "true"
elif flag == "-p":
cfg["preprocess"] = "true"
elif flag == "-r":
cfg["recurse"] = "true"
elif flag == "-a":
cfg["append." & $uniq] = param[2..^1]
elif flag == "-e":
cfg["prepend." & $uniq] = param[2..^1]
elif flag == "-l":
cfg["replace." & $uniq] = param[2..^1]
elif flag == "-o":
cfg["comment." & $uniq] = param[2..^1]
elif flag == "-s":
cfg["search." & $uniq] = param[2..^1]
elif flag == "-x":
cfg["regex." & $uniq] = param[2..^1]
elif param == "-h" or param == "-?" or param == "--help":
echo gHelp
quit(0)
uniq += 1
for file in files:
runFile(file, cfg)

View file

@ -1,7 +1,78 @@
import ospaths import distros, ospaths, strutils
import strutils
for comp in @["nimbass", "nimfuzz", "nimssl"]: var
pygonly = false
comps = @["nim7z", #"nimarchive",
"nimbass", "nimbigwig",
"nimclipboard", "nimfuzzy", "nimmonocypher",
#"nimnuklear",
"nimpcre", "nimrax", "nimssl", "nimssh2",
"nimtess2"
]
let
gccver = staticExec("gcc --version").split("\n")[0].split(" ")[^1]
nimver = staticExec("nim -v").split("\n")[0].split(" ")[3]
if nimver >= "0.19.0" and (gccver >= "5.0.0" or detectOs(MacOSX)):
comps.add("nimfastText")
if detectOs(Windows):
comps.add("nimkerberos")
if not detectOs(MacOSX):
comps.add("nimzbar")
echo "Nim version: " & nimver
echo "GCC version: " & gccver
echo "Testing comps:"
for comp in comps:
echo " " & comp
if paramCount() > 2:
for i in 3 .. paramCount():
if paramStr(i) == "--pygonly":
pygonly = true
elif paramStr(i).len() > 10 and "--comps=" in paramStr(i)[0 ..< 8]:
comps = paramStr(i)[8 .. ^1].split(",")
for comp in comps:
if not pygonly:
if not dirExists(".."/comp):
withDir(".."):
exec "git clone --depth=1 https://github.com/genotrance/" & comp
exec "nimble uninstall -y " & comp, "", ""
withDir(".."/comp): withDir(".."/comp):
exec "nimble install -y" exec "git pull"
exec "nimble test"
rmDir(comp)
exec "nimble install -y"
exec "nimble test"
exec "nimble install -y"
exec "nimble test"
when defined(windows):
if not pygonly:
if dirExists("web"/comp):
rmDir("web"/comp)
mkDir("web"/comp)
for file in listFiles(".."/comp/comp) & listFiles(".."/comp):
if file.splitFile().ext == ".nim":
cpFile(file, "web"/comp/extractFilename(file))
cpFile("web"/"nimdoc.cfg", "web"/comp/"nimdoc.cfg")
withDir("web"/comp):
for file in listFiles("."):
if file.splitFile().ext == ".nim":
if not pygonly:
exec "nim doc --git.url:. --index:on -o:" & file.changeFileExt("html") & " " & file
exec "pygmentize -f html -O full,linenos=1,anchorlinenos=True,lineanchors=L,style=vs -o " & file & ".html " & file
if not pygonly:
exec "nim buildIndex -o:index.html ."
rmFile("web"/comp/"nimdoc.cfg")

10
tests/rununittests.nim Normal file
View file

@ -0,0 +1,10 @@
import os, osproc, strutils
proc main() =
var failures = 0
for file in walkFiles(currentSourcePath().splitPath().head / "unittests/*.nim"):
let (path, fname, ext) = file.splitFile()
if fname.startswith("test"):
failures += execCmd "nim c -r " & file
quit(failures)
main()

View file

@ -0,0 +1,5 @@
import unittest
proc checkFile*(filepath, expected: string) =
let result = readFile(filepath)
check result == expected

View file

@ -0,0 +1,675 @@
/**
* @file ipc.h
* @brief Inter-process communication handling
* @author plutoo
* @copyright libnx Authors
*/
#pragma once
#include "../result.h"
#include "../arm/tls.h"
#include "../kernel/svc.h"
/// IPC input header magic
#define SFCI_MAGIC 0x49434653
/// IPC output header magic
#define SFCO_MAGIC 0x4f434653
/// IPC invalid object ID
#define IPC_INVALID_OBJECT_ID UINT32_MAX
///@name IPC request building
///@{
/// IPC command (request) structure.
#define IPC_MAX_BUFFERS 8
#define IPC_MAX_OBJECTS 8
typedef enum {
BufferType_Normal=0, ///< Regular buffer.
BufferType_Type1=1, ///< Allows ProcessMemory and shared TransferMemory.
BufferType_Invalid=2,
BufferType_Type3=3 ///< Same as Type1 except remote process is not allowed to use device-mapping.
} BufferType;
typedef enum {
BufferDirection_Send=0,
BufferDirection_Recv=1,
BufferDirection_Exch=2,
} BufferDirection;
typedef enum {
IpcCommandType_Invalid = 0,
IpcCommandType_LegacyRequest = 1,
IpcCommandType_Close = 2,
IpcCommandType_LegacyControl = 3,
IpcCommandType_Request = 4,
IpcCommandType_Control = 5,
IpcCommandType_RequestWithContext = 6,
IpcCommandType_ControlWithContext = 7,
} IpcCommandType;
typedef enum {
DomainMessageType_Invalid = 0,
DomainMessageType_SendMessage = 1,
DomainMessageType_Close = 2,
} DomainMessageType;
/// IPC domain message header.
typedef struct {
u8 Type;
u8 NumObjectIds;
u16 Length;
u32 ThisObjectId;
u32 Pad[2];
} DomainMessageHeader;
typedef struct {
size_t NumSend; // A
size_t NumRecv; // B
size_t NumExch; // W
const void* Buffers[IPC_MAX_BUFFERS];
size_t BufferSizes[IPC_MAX_BUFFERS];
BufferType BufferTypes[IPC_MAX_BUFFERS];
size_t NumStaticIn; // X
size_t NumStaticOut; // C
const void* Statics[IPC_MAX_BUFFERS];
size_t StaticSizes[IPC_MAX_BUFFERS];
u8 StaticIndices[IPC_MAX_BUFFERS];
bool SendPid;
size_t NumHandlesCopy;
size_t NumHandlesMove;
Handle Handles[IPC_MAX_OBJECTS];
size_t NumObjectIds;
u32 ObjectIds[IPC_MAX_OBJECTS];
} IpcCommand;
/**
* @brief Initializes an IPC command structure.
* @param cmd IPC command structure.
*/
static inline void ipcInitialize(IpcCommand* cmd);//{
// *cmd = (IpcCommand){0};
//}
/// IPC buffer descriptor.
typedef struct {
u32 Size; ///< Size of the buffer.
u32 Addr; ///< Lower 32-bits of the address of the buffer
u32 Packed; ///< Packed data (including higher bits of the address)
} IpcBufferDescriptor;
/// IPC static send-buffer descriptor.
typedef struct {
u32 Packed; ///< Packed data (including higher bits of the address)
u32 Addr; ///< Lower 32-bits of the address
} IpcStaticSendDescriptor;
/// IPC static receive-buffer descriptor.
typedef struct {
u32 Addr; ///< Lower 32-bits of the address of the buffer
u32 Packed; ///< Packed data (including higher bits of the address)
} IpcStaticRecvDescriptor;
/**
* @brief Adds a buffer to an IPC command structure.
* @param cmd IPC command structure.
* @param buffer Address of the buffer.
* @param size Size of the buffer.
* @param type Buffer type.
*/
static inline void ipcAddSendBuffer(IpcCommand* cmd, const void* buffer, size_t size, BufferType type);//{
// size_t off = cmd->NumSend;
// cmd->Buffers[off] = buffer;
// cmd->BufferSizes[off] = size;
// cmd->BufferTypes[off] = type;
// cmd->NumSend++;
//}
/**
* @brief Adds a receive-buffer to an IPC command structure.
* @param cmd IPC command structure.
* @param buffer Address of the buffer.
* @param size Size of the buffer.
* @param type Buffer type.
*/
static inline void ipcAddRecvBuffer(IpcCommand* cmd, void* buffer, size_t size, BufferType type);//{
// size_t off = cmd->NumSend + cmd->NumRecv;
// cmd->Buffers[off] = buffer;
// cmd->BufferSizes[off] = size;
// cmd->BufferTypes[off] = type;
// cmd->NumRecv++;
//}
/**
* @brief Adds an exchange-buffer to an IPC command structure.
* @param cmd IPC command structure.
* @param buffer Address of the buffer.
* @param size Size of the buffer.
* @param type Buffer type.
*/
static inline void ipcAddExchBuffer(IpcCommand* cmd, void* buffer, size_t size, BufferType type);//{
// size_t off = cmd->NumSend + cmd->NumRecv + cmd->NumExch;
// cmd->Buffers[off] = buffer;
// cmd->BufferSizes[off] = size;
// cmd->BufferTypes[off] = type;
// cmd->NumExch++;
//}
/**
* @brief Adds a static-buffer to an IPC command structure.
* @param cmd IPC command structure.
* @param buffer Address of the buffer.
* @param size Size of the buffer.
* @param index Index of buffer.
*/
static inline void ipcAddSendStatic(IpcCommand* cmd, const void* buffer, size_t size, u8 index);//{
// size_t off = cmd->NumStaticIn;
// cmd->Statics[off] = buffer;
// cmd->StaticSizes[off] = size;
// cmd->StaticIndices[off] = index;
// cmd->NumStaticIn++;
//}
/**
* @brief Adds a static-receive-buffer to an IPC command structure.
* @param cmd IPC command structure.
* @param buffer Address of the buffer.
* @param size Size of the buffer.
* @param index Index of buffer.
*/
static inline void ipcAddRecvStatic(IpcCommand* cmd, void* buffer, size_t size, u8 index);//{
// size_t off = cmd->NumStaticIn + cmd->NumStaticOut;
// cmd->Statics[off] = buffer;
// cmd->StaticSizes[off] = size;
// cmd->StaticIndices[off] = index;
// cmd->NumStaticOut++;
//}
/**
* @brief Adds a smart-buffer (buffer + static-buffer pair) to an IPC command structure.
* @param cmd IPC command structure.
* @param ipc_buffer_size IPC buffer size.
* @param buffer Address of the buffer.
* @param size Size of the buffer.
* @param index Index of buffer.
*/
static inline void ipcAddSendSmart(IpcCommand* cmd, size_t ipc_buffer_size, const void* buffer, size_t size, u8 index);//{
// if (ipc_buffer_size != 0 && size <= ipc_buffer_size) {
// ipcAddSendBuffer(cmd, NULL, 0, BufferType_Normal);
// ipcAddSendStatic(cmd, buffer, size, index);
// } else {
// ipcAddSendBuffer(cmd, buffer, size, BufferType_Normal);
// ipcAddSendStatic(cmd, NULL, 0, index);
// }
//}
/**
* @brief Adds a smart-receive-buffer (buffer + static-receive-buffer pair) to an IPC command structure.
* @param cmd IPC command structure.
* @param ipc_buffer_size IPC buffer size.
* @param buffer Address of the buffer.
* @param size Size of the buffer.
* @param index Index of buffer.
*/
static inline void ipcAddRecvSmart(IpcCommand* cmd, size_t ipc_buffer_size, void* buffer, size_t size, u8 index);//{
// if (ipc_buffer_size != 0 && size <= ipc_buffer_size) {
// ipcAddRecvBuffer(cmd, NULL, 0, BufferType_Normal);
// ipcAddRecvStatic(cmd, buffer, size, index);
// } else {
// ipcAddRecvBuffer(cmd, buffer, size, BufferType_Normal);
// ipcAddRecvStatic(cmd, NULL, 0, index);
// }
//}
/**
* @brief Tags an IPC command structure to send the PID.
* @param cmd IPC command structure.
*/
static inline void ipcSendPid(IpcCommand* cmd);//{
// cmd->SendPid = true;
//}
/**
* @brief Adds a copy-handle to be sent through an IPC command structure.
* @param cmd IPC command structure.
* @param h Handle to send.
* @remark The receiving process gets a copy of the handle.
*/
static inline void ipcSendHandleCopy(IpcCommand* cmd, Handle h);//{
// cmd->Handles[cmd->NumHandlesCopy++] = h;
//}
/**
* @brief Adds a move-handle to be sent through an IPC command structure.
* @param cmd IPC command structure.
* @param h Handle to send.
* @remark The sending process loses ownership of the handle, which is transferred to the receiving process.
*/
static inline void ipcSendHandleMove(IpcCommand* cmd, Handle h);//{
// cmd->Handles[cmd->NumHandlesCopy + cmd->NumHandlesMove++] = h;
//}
/**
* @brief Prepares the header of an IPC command structure.
* @param cmd IPC command structure.
* @param sizeof_raw Size in bytes of the raw data structure to embed inside the IPC request
* @return Pointer to the raw embedded data structure in the request, ready to be filled out.
*/
static inline void* ipcPrepareHeader(IpcCommand* cmd, size_t sizeof_raw);//{
// u32* buf = (u32*)armGetTls();
// size_t i;
// *buf++ = IpcCommandType_Request | (cmd->NumStaticIn << 16) | (cmd->NumSend << 20) | (cmd->NumRecv << 24) | (cmd->NumExch << 28);
//
// u32* fill_in_size_later = buf;
//
// if (cmd->NumStaticOut > 0) {
// *buf = (cmd->NumStaticOut + 2) << 10;
// }
// else {
// *buf = 0;
// }
//
// if (cmd->SendPid || cmd->NumHandlesCopy > 0 || cmd->NumHandlesMove > 0) {
// *buf++ |= 0x80000000;
// *buf++ = (!!cmd->SendPid) | (cmd->NumHandlesCopy << 1) | (cmd->NumHandlesMove << 5);
//
// if (cmd->SendPid)
// buf += 2;
//
// for (i=0; i<(cmd->NumHandlesCopy + cmd->NumHandlesMove); i++)
// *buf++ = cmd->Handles[i];
// }
// else {
// buf++;
// }
//
// for (i=0; i<cmd->NumStaticIn; i++, buf+=2) {
// IpcStaticSendDescriptor* desc = (IpcStaticSendDescriptor*) buf;
//
// uintptr_t ptr = (uintptr_t) cmd->Statics[i];
// desc->Addr = ptr;
// desc->Packed = cmd->StaticIndices[i] | (cmd->StaticSizes[i] << 16) |
// (((ptr >> 32) & 15) << 12) | (((ptr >> 36) & 15) << 6);
// }
//
// for (i=0; i<(cmd->NumSend + cmd->NumRecv + cmd->NumExch); i++, buf+=3) {
// IpcBufferDescriptor* desc = (IpcBufferDescriptor*) buf;
// desc->Size = cmd->BufferSizes[i];
//
// uintptr_t ptr = (uintptr_t) cmd->Buffers[i];
// desc->Addr = ptr;
// desc->Packed = cmd->BufferTypes[i] |
// (((ptr >> 32) & 15) << 28) | ((ptr >> 36) << 2);
// }
//
// u32 padding = ((16 - (((uintptr_t) buf) & 15)) & 15) / 4;
// u32* raw = (u32*) (buf + padding);
//
// size_t raw_size = (sizeof_raw/4) + 4;
// buf += raw_size;
//
// u16* buf_u16 = (u16*) buf;
//
// for (i=0; i<cmd->NumStaticOut; i++) {
// size_t off = cmd->NumStaticIn + i;
// size_t sz = (uintptr_t) cmd->StaticSizes[off];
//
// buf_u16[i] = (sz > 0xFFFF) ? 0 : sz;
// }
//
// size_t u16s_size = ((2*cmd->NumStaticOut) + 3)/4;
// buf += u16s_size;
// raw_size += u16s_size;
//
// *fill_in_size_later |= raw_size;
//
// for (i=0; i<cmd->NumStaticOut; i++, buf+=2) {
// IpcStaticRecvDescriptor* desc = (IpcStaticRecvDescriptor*) buf;
// size_t off = cmd->NumStaticIn + i;
//
// uintptr_t ptr = (uintptr_t) cmd->Statics[off];
// desc->Addr = ptr;
// desc->Packed = (ptr >> 32) | (cmd->StaticSizes[off] << 16);
// }
//
// return (void*) raw;
//}
/**
* @brief Dispatches an IPC request.
* @param session IPC session handle.
* @return Result code.
*/
static inline Result ipcDispatch(Handle session);//{
// return svcSendSyncRequest(session);
//}
///@}
///@name IPC response parsing
///@{
/// IPC parsed command (response) structure.
typedef struct {
IpcCommandType CommandType; ///< Type of the command
bool HasPid; ///< true if the 'Pid' field is filled out.
u64 Pid; ///< PID included in the response (only if HasPid is true)
size_t NumHandles; ///< Number of handles copied.
Handle Handles[IPC_MAX_OBJECTS]; ///< Handles.
bool WasHandleCopied[IPC_MAX_OBJECTS]; ///< true if the handle was moved, false if it was copied.
bool IsDomainMessage; ///< true if the the message is a Domain message.
DomainMessageType MessageType; ///< Type of the domain message.
u32 MessageLength; ///< Size of rawdata (for domain messages).
u32 ThisObjectId; ///< Object ID to call the command on (for domain messages).
size_t NumObjectIds; ///< Number of object IDs (for domain messages).
u32 ObjectIds[IPC_MAX_OBJECTS]; ///< Object IDs (for domain messages).
size_t NumBuffers; ///< Number of buffers in the response.
void* Buffers[IPC_MAX_BUFFERS]; ///< Pointers to the buffers.
size_t BufferSizes[IPC_MAX_BUFFERS]; ///< Sizes of the buffers.
BufferType BufferTypes[IPC_MAX_BUFFERS]; ///< Types of the buffers.
BufferDirection BufferDirections[IPC_MAX_BUFFERS]; ///< Direction of each buffer.
size_t NumStatics; ///< Number of statics in the response.
void* Statics[IPC_MAX_BUFFERS]; ///< Pointers to the statics.
size_t StaticSizes[IPC_MAX_BUFFERS]; ///< Sizes of the statics.
u8 StaticIndices[IPC_MAX_BUFFERS]; ///< Indices of the statics.
size_t NumStaticsOut; ///< Number of output statics available in the response.
void* Raw; ///< Pointer to the raw embedded data structure in the response.
void* RawWithoutPadding; ///< Pointer to the raw embedded data structure, without padding.
size_t RawSize; ///< Size of the raw embedded data.
} IpcParsedCommand;
/**
* @brief Parse an IPC command response into an IPC parsed command structure.
* @param IPC parsed command structure to fill in.
* @return Result code.
*/
static inline Result ipcParse(IpcParsedCommand* r);//{
// u32* buf = (u32*)armGetTls();
// u32 ctrl0 = *buf++;
// u32 ctrl1 = *buf++;
// size_t i;
//
// r->IsDomainMessage = false;
//
// r->CommandType = (IpcCommandType) (ctrl0 & 0xffff);
// r->HasPid = false;
// r->RawSize = (ctrl1 & 0x1ff) * 4;
// r->NumHandles = 0;
//
// r->NumStaticsOut = (ctrl1 >> 10) & 15;
// if (r->NumStaticsOut >> 1) r->NumStaticsOut--; // Value 2 -> Single descriptor
// if (r->NumStaticsOut >> 1) r->NumStaticsOut--; // Value 3+ -> (Value - 2) descriptors
//
// if (ctrl1 & 0x80000000) {
// u32 ctrl2 = *buf++;
//
// if (ctrl2 & 1) {
// r->HasPid = true;
// r->Pid = *buf++;
// r->Pid |= ((u64)(*buf++)) << 32;
// }
//
// size_t num_handles_copy = ((ctrl2 >> 1) & 15);
// size_t num_handles_move = ((ctrl2 >> 5) & 15);
//
// size_t num_handles = num_handles_copy + num_handles_move;
// u32* buf_after_handles = buf + num_handles;
//
// if (num_handles > IPC_MAX_OBJECTS)
// num_handles = IPC_MAX_OBJECTS;
//
// for (i=0; i<num_handles; i++)
// {
// r->Handles[i] = *(buf+i);
// r->WasHandleCopied[i] = (i < num_handles_copy);
// }
//
// r->NumHandles = num_handles;
// buf = buf_after_handles;
// }
//
// size_t num_statics = (ctrl0 >> 16) & 15;
// u32* buf_after_statics = buf + num_statics*2;
//
// if (num_statics > IPC_MAX_BUFFERS)
// num_statics = IPC_MAX_BUFFERS;
//
// for (i=0; i<num_statics; i++, buf+=2) {
// IpcStaticSendDescriptor* desc = (IpcStaticSendDescriptor*) buf;
// u64 packed = (u64) desc->Packed;
//
// r->Statics[i] = (void*) (desc->Addr | (((packed >> 12) & 15) << 32) | (((packed >> 6) & 15) << 36));
// r->StaticSizes[i] = packed >> 16;
// r->StaticIndices[i] = packed & 63;
// }
//
// r->NumStatics = num_statics;
// buf = buf_after_statics;
//
// size_t num_bufs_send = (ctrl0 >> 20) & 15;
// size_t num_bufs_recv = (ctrl0 >> 24) & 15;
// size_t num_bufs_exch = (ctrl0 >> 28) & 15;
//
// size_t num_bufs = num_bufs_send + num_bufs_recv + num_bufs_exch;
// r->Raw = (void*)(((uintptr_t)(buf + num_bufs*3) + 15) &~ 15);
// r->RawWithoutPadding = (void*)((uintptr_t)(buf + num_bufs*3));
//
// if (num_bufs > IPC_MAX_BUFFERS)
// num_bufs = IPC_MAX_BUFFERS;
//
// for (i=0; i<num_bufs; i++, buf+=3) {
// IpcBufferDescriptor* desc = (IpcBufferDescriptor*) buf;
// u64 packed = (u64) desc->Packed;
//
// r->Buffers[i] = (void*) (desc->Addr | ((packed >> 28) << 32) | (((packed >> 2) & 15) << 36));
// r->BufferSizes[i] = desc->Size;
// r->BufferTypes[i] = (BufferType) (packed & 3);
//
// if (i < num_bufs_send)
// r->BufferDirections[i] = BufferDirection_Send;
// else if (i < (num_bufs_send + num_bufs_recv))
// r->BufferDirections[i] = BufferDirection_Recv;
// else
// r->BufferDirections[i] = BufferDirection_Exch;
// }
//
// r->NumBuffers = num_bufs;
// return 0;
//}
/**
* @brief Queries the size of an IPC pointer buffer.
* @param session IPC session handle.
* @param size Output variable in which to store the size.
* @return Result code.
*/
static inline Result ipcQueryPointerBufferSize(Handle session, size_t *size);//{
// u32* buf = (u32*)armGetTls();
//
// buf[0] = IpcCommandType_Control;
// buf[1] = 8;
// buf[2] = 0;
// buf[3] = 0;
// buf[4] = SFCI_MAGIC;
// buf[5] = 0;
// buf[6] = 3;
// buf[7] = 0;
//
// Result rc = ipcDispatch(session);
//
// if (R_SUCCEEDED(rc)) {
// IpcParsedCommand r;
// ipcParse(&r);
//
// struct ipcQueryPointerBufferSizeResponse {
// u64 magic;
// u64 result;
// u32 size;
// } *raw = (struct ipcQueryPointerBufferSizeResponse*)r.Raw;
//
// rc = raw->result;
//
// if (R_SUCCEEDED(rc)) {
// *size = raw->size & 0xffff;
// }
// }
//
// return rc;
//}
/**
* @brief Closes the IPC session with proper clean up.
* @param session IPC session handle.
* @return Result code.
*/
static inline Result ipcCloseSession(Handle session);//{
// u32* buf = (u32*)armGetTls();
// buf[0] = IpcCommandType_Close;
// buf[1] = 0;
// return ipcDispatch(session);
//}
///@}
///@name IPC domain handling
///@{
/**
* @brief Converts an IPC session handle into a domain.
* @param session IPC session handle.
* @param object_id_out Output variable in which to store the object ID.
* @return Result code.
*/
static inline Result ipcConvertSessionToDomain(Handle session, u32* object_id_out);//{
// u32* buf = (u32*)armGetTls();
//
// buf[0] = IpcCommandType_Control;
// buf[1] = 8;
// buf[4] = SFCI_MAGIC;
// buf[5] = 0;
// buf[6] = 0;
// buf[7] = 0;
//
// Result rc = ipcDispatch(session);
//
// if (R_SUCCEEDED(rc)) {
// IpcParsedCommand r;
// ipcParse(&r);
//
// struct ipcConvertSessionToDomainResponse {
// u64 magic;
// u64 result;
// u32 object_id;
// } *raw = (struct ipcConvertSessionToDomainResponse*)r.Raw;
//
// rc = raw->result;
//
// if (R_SUCCEEDED(rc)) {
// *object_id_out = raw->object_id;
// }
// }
//
// return rc;
//}
/**
* @brief Adds an object ID to be sent through an IPC domain command structure.
* @param cmd IPC domain command structure.
* @param object_id Object ID to send.
*/
static inline void ipcSendObjectId(IpcCommand* cmd, u32 object_id);//{
// cmd->ObjectIds[cmd->NumObjectIds++] = object_id;
//}
/**
* @brief Prepares the header of an IPC command structure (domain version).
* @param cmd IPC command structure.
* @param sizeof_raw Size in bytes of the raw data structure to embed inside the IPC request
* @oaram object_id Domain object ID.
* @return Pointer to the raw embedded data structure in the request, ready to be filled out.
*/
static inline void* ipcPrepareHeaderForDomain(IpcCommand* cmd, size_t sizeof_raw, u32 object_id);//{
// void* raw = ipcPrepareHeader(cmd, sizeof_raw + sizeof(DomainMessageHeader));
// DomainMessageHeader* hdr = (DomainMessageHeader*) raw;
// u32 *object_ids = (u32*)(((uintptr_t) raw) + sizeof(DomainMessageHeader) + sizeof_raw);
//
// hdr->Type = DomainMessageType_SendMessage;
// hdr->NumObjectIds = (u8)cmd->NumObjectIds;
// hdr->Length = sizeof_raw;
// hdr->ThisObjectId = object_id;
// hdr->Pad[0] = hdr->Pad[1] = 0;
//
// for(size_t i = 0; i < cmd->NumObjectIds; i++)
// object_ids[i] = cmd->ObjectIds[i];
// return (void*)(((uintptr_t) raw) + sizeof(DomainMessageHeader));
//}
/**
* @brief Parse an IPC command response into an IPC parsed command structure (domain version).
* @param IPC parsed command structure to fill in.
* @return Result code.
*/
static inline Result ipcParseForDomain(IpcParsedCommand* r);//{
// Result rc = ipcParse(r);
// DomainMessageHeader *hdr;
// u32 *object_ids;
// if(R_FAILED(rc))
// return rc;
//
// hdr = (DomainMessageHeader*) r->Raw;
// object_ids = (u32*)(((uintptr_t) hdr) + sizeof(DomainMessageHeader) + hdr->Length);
// r->Raw = (void*)(((uintptr_t) r->Raw) + sizeof(DomainMessageHeader));
//
// r->IsDomainMessage = true;
// r->MessageType = (DomainMessageType)(hdr->Type);
// switch (r->MessageType) {
// case DomainMessageType_SendMessage:
// case DomainMessageType_Close:
// break;
// default:
// return MAKERESULT(Module_Libnx, LibnxError_DomainMessageUnknownType);
// }
// r->ThisObjectId = hdr->ThisObjectId;
// r->NumObjectIds = hdr->NumObjectIds > 8 ? 8 : hdr->NumObjectIds;
// if ((uintptr_t)object_ids + sizeof(u32) * r->NumObjectIds - (uintptr_t)armGetTls() >= 0x100) {
// return MAKERESULT(Module_Libnx, LibnxError_DomainMessageTooManyObjectIds);
// }
// for(size_t i = 0; i < r->NumObjectIds; i++)
// r->ObjectIds[i] = object_ids[i];
//
// return rc;
//}
/**
* @brief Closes a domain object by ID.
* @param session IPC session handle.
* @param object_id ID of the object to close.
* @return Result code.
*/
static inline Result ipcCloseObjectById(Handle session, u32 object_id);//{
// IpcCommand c;
// DomainMessageHeader* hdr;
//
// ipcInitialize(&c);
// hdr = (DomainMessageHeader*)ipcPrepareHeader(&c, sizeof(DomainMessageHeader));
//
// hdr->Type = 2;
// hdr->NumObjectIds = 0;
// hdr->Length = 0;
// hdr->ThisObjectId = object_id;
// hdr->Pad[0] = hdr->Pad[1] = 0;
//
// return ipcDispatch(session); // this command has no associated response
//}
///@}

View file

@ -0,0 +1,68 @@
/**
* @file condvar.h
* @brief Condition variable synchronization primitive.
* @author plutoo
* @copyright libnx Authors
*/
#pragma once
#include "../types.h"
#include "../kernel/mutex.h"
/// Condition variable structure.
typedef struct {
u32 tag;
Mutex* mutex;
} CondVar;
/**
* @brief Initializes a condition variable.
* @param[in] c Condition variable object.
* @param[in] m Mutex object to use inside the condition variable.
*/
void condvarInit(CondVar* c, Mutex* m);
/**
* @brief Waits on a condition variable with a timeout.
* @param[in] c Condition variable object.
* @param[in] timeout Timeout in nanoseconds.
* @return Result code (0xEA01 on timeout).
* @remark On function return, the underlying mutex is acquired.
*/
Result condvarWaitTimeout(CondVar* c, u64 timeout);
/**
* @brief Waits on a condition variable.
* @param[in] c Condition variable object.
* @return Result code.
* @remark On function return, the underlying mutex is acquired.
*/
static inline Result condvarWait(CondVar* c);//{
// return condvarWaitTimeout(c, -1ull);
//}
/**
* @brief Wakes up up to the specified number of threads waiting on a condition variable.
* @param[in] c Condition variable object.
* @param[in] num Maximum number of threads to wake up (or -1 to wake them all up).
* @return Result code.
*/
Result condvarWake(CondVar* c, int num);
/**
* @brief Wakes up a single thread waiting on a condition variable.
* @param[in] c Condition variable object.
* @return Result code.
*/
static inline Result condvarWakeOne(CondVar* c);//{
// return condvarWake(c, 1);
//}
/**
* @brief Wakes up all thread waiting on a condition variable.
* @param[in] c Condition variable object.
* @return Result code.
*/
static inline Result condvarWakeAll(CondVar* c);//{
// return condvarWake(c, -1);
//}

View file

@ -0,0 +1,675 @@
/**
* @file ipc.h
* @brief Inter-process communication handling
* @author plutoo
* @copyright libnx Authors
*/
#pragma once
#include "../result.h"
#include "../arm/tls.h"
#include "../kernel/svc.h"
/// IPC input header magic
#define SFCI_MAGIC 0x49434653
/// IPC output header magic
#define SFCO_MAGIC 0x4f434653
/// IPC invalid object ID
#define IPC_INVALID_OBJECT_ID UINT32_MAX
///@name IPC request building
///@{
/// IPC command (request) structure.
#define IPC_MAX_BUFFERS 8
#define IPC_MAX_OBJECTS 8
typedef enum {
BufferType_Normal=0, ///< Regular buffer.
BufferType_Type1=1, ///< Allows ProcessMemory and shared TransferMemory.
BufferType_Invalid=2,
BufferType_Type3=3 ///< Same as Type1 except remote process is not allowed to use device-mapping.
} BufferType;
typedef enum {
BufferDirection_Send=0,
BufferDirection_Recv=1,
BufferDirection_Exch=2,
} BufferDirection;
typedef enum {
IpcCommandType_Invalid = 0,
IpcCommandType_LegacyRequest = 1,
IpcCommandType_Close = 2,
IpcCommandType_LegacyControl = 3,
IpcCommandType_Request = 4,
IpcCommandType_Control = 5,
IpcCommandType_RequestWithContext = 6,
IpcCommandType_ControlWithContext = 7,
} IpcCommandType;
typedef enum {
DomainMessageType_Invalid = 0,
DomainMessageType_SendMessage = 1,
DomainMessageType_Close = 2,
} DomainMessageType;
/// IPC domain message header.
typedef struct {
u8 Type;
u8 NumObjectIds;
u16 Length;
u32 ThisObjectId;
u32 Pad[2];
} DomainMessageHeader;
typedef struct {
size_t NumSend; // A
size_t NumRecv; // B
size_t NumExch; // W
const void* Buffers[IPC_MAX_BUFFERS];
size_t BufferSizes[IPC_MAX_BUFFERS];
BufferType BufferTypes[IPC_MAX_BUFFERS];
size_t NumStaticIn; // X
size_t NumStaticOut; // C
const void* Statics[IPC_MAX_BUFFERS];
size_t StaticSizes[IPC_MAX_BUFFERS];
u8 StaticIndices[IPC_MAX_BUFFERS];
bool SendPid;
size_t NumHandlesCopy;
size_t NumHandlesMove;
Handle Handles[IPC_MAX_OBJECTS];
size_t NumObjectIds;
u32 ObjectIds[IPC_MAX_OBJECTS];
} IpcCommand;
/**
* @brief Initializes an IPC command structure.
* @param cmd IPC command structure.
*/
static inline void ipcInitialize(IpcCommand* cmd) {
*cmd = (IpcCommand){0};
}
/// IPC buffer descriptor.
typedef struct {
u32 Size; ///< Size of the buffer.
u32 Addr; ///< Lower 32-bits of the address of the buffer
u32 Packed; ///< Packed data (including higher bits of the address)
} IpcBufferDescriptor;
/// IPC static send-buffer descriptor.
typedef struct {
u32 Packed; ///< Packed data (including higher bits of the address)
u32 Addr; ///< Lower 32-bits of the address
} IpcStaticSendDescriptor;
/// IPC static receive-buffer descriptor.
typedef struct {
u32 Addr; ///< Lower 32-bits of the address of the buffer
u32 Packed; ///< Packed data (including higher bits of the address)
} IpcStaticRecvDescriptor;
/**
* @brief Adds a buffer to an IPC command structure.
* @param cmd IPC command structure.
* @param buffer Address of the buffer.
* @param size Size of the buffer.
* @param type Buffer type.
*/
static inline void ipcAddSendBuffer(IpcCommand* cmd, const void* buffer, size_t size, BufferType type) {
size_t off = cmd->NumSend;
cmd->Buffers[off] = buffer;
cmd->BufferSizes[off] = size;
cmd->BufferTypes[off] = type;
cmd->NumSend++;
}
/**
* @brief Adds a receive-buffer to an IPC command structure.
* @param cmd IPC command structure.
* @param buffer Address of the buffer.
* @param size Size of the buffer.
* @param type Buffer type.
*/
static inline void ipcAddRecvBuffer(IpcCommand* cmd, void* buffer, size_t size, BufferType type) {
size_t off = cmd->NumSend + cmd->NumRecv;
cmd->Buffers[off] = buffer;
cmd->BufferSizes[off] = size;
cmd->BufferTypes[off] = type;
cmd->NumRecv++;
}
/**
* @brief Adds an exchange-buffer to an IPC command structure.
* @param cmd IPC command structure.
* @param buffer Address of the buffer.
* @param size Size of the buffer.
* @param type Buffer type.
*/
static inline void ipcAddExchBuffer(IpcCommand* cmd, void* buffer, size_t size, BufferType type) {
size_t off = cmd->NumSend + cmd->NumRecv + cmd->NumExch;
cmd->Buffers[off] = buffer;
cmd->BufferSizes[off] = size;
cmd->BufferTypes[off] = type;
cmd->NumExch++;
}
/**
* @brief Adds a static-buffer to an IPC command structure.
* @param cmd IPC command structure.
* @param buffer Address of the buffer.
* @param size Size of the buffer.
* @param index Index of buffer.
*/
static inline void ipcAddSendStatic(IpcCommand* cmd, const void* buffer, size_t size, u8 index) {
size_t off = cmd->NumStaticIn;
cmd->Statics[off] = buffer;
cmd->StaticSizes[off] = size;
cmd->StaticIndices[off] = index;
cmd->NumStaticIn++;
}
/**
* @brief Adds a static-receive-buffer to an IPC command structure.
* @param cmd IPC command structure.
* @param buffer Address of the buffer.
* @param size Size of the buffer.
* @param index Index of buffer.
*/
static inline void ipcAddRecvStatic(IpcCommand* cmd, void* buffer, size_t size, u8 index) {
size_t off = cmd->NumStaticIn + cmd->NumStaticOut;
cmd->Statics[off] = buffer;
cmd->StaticSizes[off] = size;
cmd->StaticIndices[off] = index;
cmd->NumStaticOut++;
}
/**
* @brief Adds a smart-buffer (buffer + static-buffer pair) to an IPC command structure.
* @param cmd IPC command structure.
* @param ipc_buffer_size IPC buffer size.
* @param buffer Address of the buffer.
* @param size Size of the buffer.
* @param index Index of buffer.
*/
static inline void ipcAddSendSmart(IpcCommand* cmd, size_t ipc_buffer_size, const void* buffer, size_t size, u8 index) {
if (ipc_buffer_size != 0 && size <= ipc_buffer_size) {
ipcAddSendBuffer(cmd, NULL, 0, BufferType_Normal);
ipcAddSendStatic(cmd, buffer, size, index);
} else {
ipcAddSendBuffer(cmd, buffer, size, BufferType_Normal);
ipcAddSendStatic(cmd, NULL, 0, index);
}
}
/**
* @brief Adds a smart-receive-buffer (buffer + static-receive-buffer pair) to an IPC command structure.
* @param cmd IPC command structure.
* @param ipc_buffer_size IPC buffer size.
* @param buffer Address of the buffer.
* @param size Size of the buffer.
* @param index Index of buffer.
*/
static inline void ipcAddRecvSmart(IpcCommand* cmd, size_t ipc_buffer_size, void* buffer, size_t size, u8 index) {
if (ipc_buffer_size != 0 && size <= ipc_buffer_size) {
ipcAddRecvBuffer(cmd, NULL, 0, BufferType_Normal);
ipcAddRecvStatic(cmd, buffer, size, index);
} else {
ipcAddRecvBuffer(cmd, buffer, size, BufferType_Normal);
ipcAddRecvStatic(cmd, NULL, 0, index);
}
}
/**
* @brief Tags an IPC command structure to send the PID.
* @param cmd IPC command structure.
*/
static inline void ipcSendPid(IpcCommand* cmd) {
cmd->SendPid = true;
}
/**
* @brief Adds a copy-handle to be sent through an IPC command structure.
* @param cmd IPC command structure.
* @param h Handle to send.
* @remark The receiving process gets a copy of the handle.
*/
static inline void ipcSendHandleCopy(IpcCommand* cmd, Handle h) {
cmd->Handles[cmd->NumHandlesCopy++] = h;
}
/**
* @brief Adds a move-handle to be sent through an IPC command structure.
* @param cmd IPC command structure.
* @param h Handle to send.
* @remark The sending process loses ownership of the handle, which is transferred to the receiving process.
*/
static inline void ipcSendHandleMove(IpcCommand* cmd, Handle h) {
cmd->Handles[cmd->NumHandlesCopy + cmd->NumHandlesMove++] = h;
}
/**
* @brief Prepares the header of an IPC command structure.
* @param cmd IPC command structure.
* @param sizeof_raw Size in bytes of the raw data structure to embed inside the IPC request
* @return Pointer to the raw embedded data structure in the request, ready to be filled out.
*/
static inline void* ipcPrepareHeader(IpcCommand* cmd, size_t sizeof_raw) {
u32* buf = (u32*)armGetTls();
size_t i;
*buf++ = IpcCommandType_Request | (cmd->NumStaticIn << 16) | (cmd->NumSend << 20) | (cmd->NumRecv << 24) | (cmd->NumExch << 28);
u32* fill_in_size_later = buf;
if (cmd->NumStaticOut > 0) {
*buf = (cmd->NumStaticOut + 2) << 10;
}
else {
*buf = 0;
}
if (cmd->SendPid || cmd->NumHandlesCopy > 0 || cmd->NumHandlesMove > 0) {
*buf++ |= 0x80000000;
*buf++ = (!!cmd->SendPid) | (cmd->NumHandlesCopy << 1) | (cmd->NumHandlesMove << 5);
if (cmd->SendPid)
buf += 2;
for (i=0; i<(cmd->NumHandlesCopy + cmd->NumHandlesMove); i++)
*buf++ = cmd->Handles[i];
}
else {
buf++;
}
for (i=0; i<cmd->NumStaticIn; i++, buf+=2) {
IpcStaticSendDescriptor* desc = (IpcStaticSendDescriptor*) buf;
uintptr_t ptr = (uintptr_t) cmd->Statics[i];
desc->Addr = ptr;
desc->Packed = cmd->StaticIndices[i] | (cmd->StaticSizes[i] << 16) |
(((ptr >> 32) & 15) << 12) | (((ptr >> 36) & 15) << 6);
}
for (i=0; i<(cmd->NumSend + cmd->NumRecv + cmd->NumExch); i++, buf+=3) {
IpcBufferDescriptor* desc = (IpcBufferDescriptor*) buf;
desc->Size = cmd->BufferSizes[i];
uintptr_t ptr = (uintptr_t) cmd->Buffers[i];
desc->Addr = ptr;
desc->Packed = cmd->BufferTypes[i] |
(((ptr >> 32) & 15) << 28) | ((ptr >> 36) << 2);
}
u32 padding = ((16 - (((uintptr_t) buf) & 15)) & 15) / 4;
u32* raw = (u32*) (buf + padding);
size_t raw_size = (sizeof_raw/4) + 4;
buf += raw_size;
u16* buf_u16 = (u16*) buf;
for (i=0; i<cmd->NumStaticOut; i++) {
size_t off = cmd->NumStaticIn + i;
size_t sz = (uintptr_t) cmd->StaticSizes[off];
buf_u16[i] = (sz > 0xFFFF) ? 0 : sz;
}
size_t u16s_size = ((2*cmd->NumStaticOut) + 3)/4;
buf += u16s_size;
raw_size += u16s_size;
*fill_in_size_later |= raw_size;
for (i=0; i<cmd->NumStaticOut; i++, buf+=2) {
IpcStaticRecvDescriptor* desc = (IpcStaticRecvDescriptor*) buf;
size_t off = cmd->NumStaticIn + i;
uintptr_t ptr = (uintptr_t) cmd->Statics[off];
desc->Addr = ptr;
desc->Packed = (ptr >> 32) | (cmd->StaticSizes[off] << 16);
}
return (void*) raw;
}
/**
* @brief Dispatches an IPC request.
* @param session IPC session handle.
* @return Result code.
*/
static inline Result ipcDispatch(Handle session) {
return svcSendSyncRequest(session);
}
///@}
///@name IPC response parsing
///@{
/// IPC parsed command (response) structure.
typedef struct {
IpcCommandType CommandType; ///< Type of the command
bool HasPid; ///< true if the 'Pid' field is filled out.
u64 Pid; ///< PID included in the response (only if HasPid is true)
size_t NumHandles; ///< Number of handles copied.
Handle Handles[IPC_MAX_OBJECTS]; ///< Handles.
bool WasHandleCopied[IPC_MAX_OBJECTS]; ///< true if the handle was moved, false if it was copied.
bool IsDomainMessage; ///< true if the the message is a Domain message.
DomainMessageType MessageType; ///< Type of the domain message.
u32 MessageLength; ///< Size of rawdata (for domain messages).
u32 ThisObjectId; ///< Object ID to call the command on (for domain messages).
size_t NumObjectIds; ///< Number of object IDs (for domain messages).
u32 ObjectIds[IPC_MAX_OBJECTS]; ///< Object IDs (for domain messages).
size_t NumBuffers; ///< Number of buffers in the response.
void* Buffers[IPC_MAX_BUFFERS]; ///< Pointers to the buffers.
size_t BufferSizes[IPC_MAX_BUFFERS]; ///< Sizes of the buffers.
BufferType BufferTypes[IPC_MAX_BUFFERS]; ///< Types of the buffers.
BufferDirection BufferDirections[IPC_MAX_BUFFERS]; ///< Direction of each buffer.
size_t NumStatics; ///< Number of statics in the response.
void* Statics[IPC_MAX_BUFFERS]; ///< Pointers to the statics.
size_t StaticSizes[IPC_MAX_BUFFERS]; ///< Sizes of the statics.
u8 StaticIndices[IPC_MAX_BUFFERS]; ///< Indices of the statics.
size_t NumStaticsOut; ///< Number of output statics available in the response.
void* Raw; ///< Pointer to the raw embedded data structure in the response.
void* RawWithoutPadding; ///< Pointer to the raw embedded data structure, without padding.
size_t RawSize; ///< Size of the raw embedded data.
} IpcParsedCommand;
/**
* @brief Parse an IPC command response into an IPC parsed command structure.
* @param IPC parsed command structure to fill in.
* @return Result code.
*/
static inline Result ipcParse(IpcParsedCommand* r) {
u32* buf = (u32*)armGetTls();
u32 ctrl0 = *buf++;
u32 ctrl1 = *buf++;
size_t i;
r->IsDomainMessage = false;
r->CommandType = (IpcCommandType) (ctrl0 & 0xffff);
r->HasPid = false;
r->RawSize = (ctrl1 & 0x1ff) * 4;
r->NumHandles = 0;
r->NumStaticsOut = (ctrl1 >> 10) & 15;
if (r->NumStaticsOut >> 1) r->NumStaticsOut--; // Value 2 -> Single descriptor
if (r->NumStaticsOut >> 1) r->NumStaticsOut--; // Value 3+ -> (Value - 2) descriptors
if (ctrl1 & 0x80000000) {
u32 ctrl2 = *buf++;
if (ctrl2 & 1) {
r->HasPid = true;
r->Pid = *buf++;
r->Pid |= ((u64)(*buf++)) << 32;
}
size_t num_handles_copy = ((ctrl2 >> 1) & 15);
size_t num_handles_move = ((ctrl2 >> 5) & 15);
size_t num_handles = num_handles_copy + num_handles_move;
u32* buf_after_handles = buf + num_handles;
if (num_handles > IPC_MAX_OBJECTS)
num_handles = IPC_MAX_OBJECTS;
for (i=0; i<num_handles; i++)
{
r->Handles[i] = *(buf+i);
r->WasHandleCopied[i] = (i < num_handles_copy);
}
r->NumHandles = num_handles;
buf = buf_after_handles;
}
size_t num_statics = (ctrl0 >> 16) & 15;
u32* buf_after_statics = buf + num_statics*2;
if (num_statics > IPC_MAX_BUFFERS)
num_statics = IPC_MAX_BUFFERS;
for (i=0; i<num_statics; i++, buf+=2) {
IpcStaticSendDescriptor* desc = (IpcStaticSendDescriptor*) buf;
u64 packed = (u64) desc->Packed;
r->Statics[i] = (void*) (desc->Addr | (((packed >> 12) & 15) << 32) | (((packed >> 6) & 15) << 36));
r->StaticSizes[i] = packed >> 16;
r->StaticIndices[i] = packed & 63;
}
r->NumStatics = num_statics;
buf = buf_after_statics;
size_t num_bufs_send = (ctrl0 >> 20) & 15;
size_t num_bufs_recv = (ctrl0 >> 24) & 15;
size_t num_bufs_exch = (ctrl0 >> 28) & 15;
size_t num_bufs = num_bufs_send + num_bufs_recv + num_bufs_exch;
r->Raw = (void*)(((uintptr_t)(buf + num_bufs*3) + 15) &~ 15);
r->RawWithoutPadding = (void*)((uintptr_t)(buf + num_bufs*3));
if (num_bufs > IPC_MAX_BUFFERS)
num_bufs = IPC_MAX_BUFFERS;
for (i=0; i<num_bufs; i++, buf+=3) {
IpcBufferDescriptor* desc = (IpcBufferDescriptor*) buf;
u64 packed = (u64) desc->Packed;
r->Buffers[i] = (void*) (desc->Addr | ((packed >> 28) << 32) | (((packed >> 2) & 15) << 36));
r->BufferSizes[i] = desc->Size;
r->BufferTypes[i] = (BufferType) (packed & 3);
if (i < num_bufs_send)
r->BufferDirections[i] = BufferDirection_Send;
else if (i < (num_bufs_send + num_bufs_recv))
r->BufferDirections[i] = BufferDirection_Recv;
else
r->BufferDirections[i] = BufferDirection_Exch;
}
r->NumBuffers = num_bufs;
return 0;
}
/**
* @brief Queries the size of an IPC pointer buffer.
* @param session IPC session handle.
* @param size Output variable in which to store the size.
* @return Result code.
*/
static inline Result ipcQueryPointerBufferSize(Handle session, size_t *size) {
u32* buf = (u32*)armGetTls();
buf[0] = IpcCommandType_Control;
buf[1] = 8;
buf[2] = 0;
buf[3] = 0;
buf[4] = SFCI_MAGIC;
buf[5] = 0;
buf[6] = 3;
buf[7] = 0;
Result rc = ipcDispatch(session);
if (R_SUCCEEDED(rc)) {
IpcParsedCommand r;
ipcParse(&r);
struct ipcQueryPointerBufferSizeResponse {
u64 magic;
u64 result;
u32 size;
} *raw = (struct ipcQueryPointerBufferSizeResponse*)r.Raw;
rc = raw->result;
if (R_SUCCEEDED(rc)) {
*size = raw->size & 0xffff;
}
}
return rc;
}
/**
* @brief Closes the IPC session with proper clean up.
* @param session IPC session handle.
* @return Result code.
*/
static inline Result ipcCloseSession(Handle session) {
u32* buf = (u32*)armGetTls();
buf[0] = IpcCommandType_Close;
buf[1] = 0;
return ipcDispatch(session);
}
///@}
///@name IPC domain handling
///@{
/**
* @brief Converts an IPC session handle into a domain.
* @param session IPC session handle.
* @param object_id_out Output variable in which to store the object ID.
* @return Result code.
*/
static inline Result ipcConvertSessionToDomain(Handle session, u32* object_id_out) {
u32* buf = (u32*)armGetTls();
buf[0] = IpcCommandType_Control;
buf[1] = 8;
buf[4] = SFCI_MAGIC;
buf[5] = 0;
buf[6] = 0;
buf[7] = 0;
Result rc = ipcDispatch(session);
if (R_SUCCEEDED(rc)) {
IpcParsedCommand r;
ipcParse(&r);
struct ipcConvertSessionToDomainResponse {
u64 magic;
u64 result;
u32 object_id;
} *raw = (struct ipcConvertSessionToDomainResponse*)r.Raw;
rc = raw->result;
if (R_SUCCEEDED(rc)) {
*object_id_out = raw->object_id;
}
}
return rc;
}
/**
* @brief Adds an object ID to be sent through an IPC domain command structure.
* @param cmd IPC domain command structure.
* @param object_id Object ID to send.
*/
static inline void ipcSendObjectId(IpcCommand* cmd, u32 object_id) {
cmd->ObjectIds[cmd->NumObjectIds++] = object_id;
}
/**
* @brief Prepares the header of an IPC command structure (domain version).
* @param cmd IPC command structure.
* @param sizeof_raw Size in bytes of the raw data structure to embed inside the IPC request
* @oaram object_id Domain object ID.
* @return Pointer to the raw embedded data structure in the request, ready to be filled out.
*/
static inline void* ipcPrepareHeaderForDomain(IpcCommand* cmd, size_t sizeof_raw, u32 object_id) {
void* raw = ipcPrepareHeader(cmd, sizeof_raw + sizeof(DomainMessageHeader));
DomainMessageHeader* hdr = (DomainMessageHeader*) raw;
u32 *object_ids = (u32*)(((uintptr_t) raw) + sizeof(DomainMessageHeader) + sizeof_raw);
hdr->Type = DomainMessageType_SendMessage;
hdr->NumObjectIds = (u8)cmd->NumObjectIds;
hdr->Length = sizeof_raw;
hdr->ThisObjectId = object_id;
hdr->Pad[0] = hdr->Pad[1] = 0;
for(size_t i = 0; i < cmd->NumObjectIds; i++)
object_ids[i] = cmd->ObjectIds[i];
return (void*)(((uintptr_t) raw) + sizeof(DomainMessageHeader));
}
/**
* @brief Parse an IPC command response into an IPC parsed command structure (domain version).
* @param IPC parsed command structure to fill in.
* @return Result code.
*/
static inline Result ipcParseForDomain(IpcParsedCommand* r) {
Result rc = ipcParse(r);
DomainMessageHeader *hdr;
u32 *object_ids;
if(R_FAILED(rc))
return rc;
hdr = (DomainMessageHeader*) r->Raw;
object_ids = (u32*)(((uintptr_t) hdr) + sizeof(DomainMessageHeader) + hdr->Length);
r->Raw = (void*)(((uintptr_t) r->Raw) + sizeof(DomainMessageHeader));
r->IsDomainMessage = true;
r->MessageType = (DomainMessageType)(hdr->Type);
switch (r->MessageType) {
case DomainMessageType_SendMessage:
case DomainMessageType_Close:
break;
default:
return MAKERESULT(Module_Libnx, LibnxError_DomainMessageUnknownType);
}
r->ThisObjectId = hdr->ThisObjectId;
r->NumObjectIds = hdr->NumObjectIds > 8 ? 8 : hdr->NumObjectIds;
if ((uintptr_t)object_ids + sizeof(u32) * r->NumObjectIds - (uintptr_t)armGetTls() >= 0x100) {
return MAKERESULT(Module_Libnx, LibnxError_DomainMessageTooManyObjectIds);
}
for(size_t i = 0; i < r->NumObjectIds; i++)
r->ObjectIds[i] = object_ids[i];
return rc;
}
/**
* @brief Closes a domain object by ID.
* @param session IPC session handle.
* @param object_id ID of the object to close.
* @return Result code.
*/
static inline Result ipcCloseObjectById(Handle session, u32 object_id) {
IpcCommand c;
DomainMessageHeader* hdr;
ipcInitialize(&c);
hdr = (DomainMessageHeader*)ipcPrepareHeader(&c, sizeof(DomainMessageHeader));
hdr->Type = 2;
hdr->NumObjectIds = 0;
hdr->Length = 0;
hdr->ThisObjectId = object_id;
hdr->Pad[0] = hdr->Pad[1] = 0;
return ipcDispatch(session); // this command has no associated response
}
///@}

View file

@ -0,0 +1,71 @@
/**
* @file condvar.h
* @brief Condition variable synchronization primitive.
* @author plutoo
* @copyright libnx Authors
*/
#pragma once
#include "../types.h"
#include "../kernel/mutex.h"
/// Condition variable structure.
typedef struct {
u32 tag;
Mutex* mutex;
} CondVar;
/**
* @brief Initializes a condition variable.
* @param[in] c Condition variable object.
* @param[in] m Mutex object to use inside the condition variable.
*/
void condvarInit(CondVar* c, Mutex* m);
/**
* @brief Waits on a condition variable with a timeout.
* @param[in] c Condition variable object.
* @param[in] timeout Timeout in nanoseconds.
* @return Result code (0xEA01 on timeout).
* @remark On function return, the underlying mutex is acquired.
*/
Result condvarWaitTimeout(CondVar* c, u64 timeout);
/**
* @brief Waits on a condition variable.
* @param[in] c Condition variable object.
* @return Result code.
* @remark On function return, the underlying mutex is acquired.
*/
static inline Result condvarWait(CondVar* c)
{
return condvarWaitTimeout(c, -1ull);
}
/**
* @brief Wakes up up to the specified number of threads waiting on a condition variable.
* @param[in] c Condition variable object.
* @param[in] num Maximum number of threads to wake up (or -1 to wake them all up).
* @return Result code.
*/
Result condvarWake(CondVar* c, int num);
/**
* @brief Wakes up a single thread waiting on a condition variable.
* @param[in] c Condition variable object.
* @return Result code.
*/
static inline Result condvarWakeOne(CondVar* c)
{
return condvarWake(c, 1);
}
/**
* @brief Wakes up all thread waiting on a condition variable.
* @param[in] c Condition variable object.
* @return Result code.
*/
static inline Result condvarWakeAll(CondVar* c)
{
return condvarWake(c, -1);
}

View file

@ -0,0 +1,68 @@
/**
* @file condvar.h
* @brief Condition variable synchronization primitive.
* @author plutoo
* @copyright libnx Authors
*/
#pragma once
#include "../types.h"
#include "../kernel/mutex.h"
/// Condition variable structure.
typedef struct {
u32 tag;
Mutex* mutex;
} CondVar;
/**
* @brief Initializes a condition variable.
* @param[in] c Condition variable object.
* @param[in] m Mutex object to use inside the condition variable.
*/
void condvarInit(CondVar* c, Mutex* m);
/**
* @brief Waits on a condition variable with a timeout.
* @param[in] c Condition variable object.
* @param[in] timeout Timeout in nanoseconds.
* @return Result code (0xEA01 on timeout).
* @remark On function return, the underlying mutex is acquired.
*/
Result condvarWaitTimeout(CondVar* c, u64 timeout);
/**
* @brief Waits on a condition variable.
* @param[in] c Condition variable object.
* @return Result code.
* @remark On function return, the underlying mutex is acquired.
*/
static inline Result condvarWait(CondVar* c) {
return condvarWaitTimeout(c, -1ull);
}
/**
* @brief Wakes up up to the specified number of threads waiting on a condition variable.
* @param[in] c Condition variable object.
* @param[in] num Maximum number of threads to wake up (or -1 to wake them all up).
* @return Result code.
*/
Result condvarWake(CondVar* c, int num);
/**
* @brief Wakes up a single thread waiting on a condition variable.
* @param[in] c Condition variable object.
* @return Result code.
*/
static inline Result condvarWakeOne(CondVar* c) {
return condvarWake(c, 1);
}
/**
* @brief Wakes up all thread waiting on a condition variable.
* @param[in] c Condition variable object.
* @return Result code.
*/
static inline Result condvarWakeAll(CondVar* c) {
return condvarWake(c, -1);
}

1
tests/unittests/nim.cfg Normal file
View file

@ -0,0 +1 @@
--path="../../src"

View file

@ -0,0 +1,209 @@
import nimgen/fileops, common, regex, os
import unittest
let testFileContent = """
this is text
this is text
replace me
prepend me
end
"""
let prependMiddleExpected = """
this is text
this is text
replace me
prepended data
prepend me
end
"""
let prependEndExpected = """
this is text
this is text
replace me
prepend me
data
end
"""
let appendEndExpected = """
this is text
this is text
replace me
prepend me
end
data
"""
let appendMiddleExpected = """
this is data
text
this is text
replace me
prepend me
end
"""
let freplaceDefaultExpected = """
replace me
prepend me
end
"""
let freplaceWithExpected = """
this is text
this is text
foobar
prepend me
end
"""
let freplaceRegexExpected = """
foobar
foobar
replace me
prepend me
end
"""
let commentExpected = """
this is text
this is text
//replace me
//prepend me
//end
"""
let commentMiddleExpected = """
this //is text
//this is text
replace me
prepend me
end
"""
let dataDir = currentSourcePath().splitPath().head / "data"
let testfilename = dataDir / "testing.txt"
suite "test file ops":
if not dataDir.dirExists():
dataDir.createDir()
setup:
writeFile(testfilename, testFileContent)
################### Prepend #######################
test "prepend at beginning of file":
prepend(testfilename, "data\n")
let expected = "data\n" & testFileContent
testfilename.checkFile(expected)
test "prepend at middle of file":
prepend(testfilename, "prepended data\n", "prepend me")
testfilename.checkFile(prependMiddleExpected)
test "prepend at end of file":
prepend(testfilename, "data\n", "end\n")
testfilename.checkFile(prependEndExpected)
################### Pipe #########################
test "pipe command into file":
when defined(windows):
pipe(testfilename, "ECHO foo")
testfilename.checkFile("foo")
else:
pipe(testfilename, "cat $file | grep 'this is text'")
testfilename.checkFile("this is text\nthis is text")
################# Append #########################
test "append file end":
append(testfilename, "data\n")
testfilename.checkFile(appendEndExpected)
test "append file middle":
append(testfilename, " data\n", "this is")
testfilename.checkFile(appendMiddleExpected)
################# FReplace #########################
test "freplace default empty":
freplace(testfilename, "this is text")
testfilename.checkFile(freplaceDefaultExpected)
test "freplace with content":
freplace(testfilename, "replace me", "foobar")
testfilename.checkFile(freplaceWithExpected)
test "freplace regex":
freplace(testfilename, re"this .*", "foobar")
testfilename.checkFile(freplaceRegexExpected)
####################### Comment ######################
test "comment":
comment(testfilename, "replace me", "3")
testfilename.checkFile(commentExpected)
test "comment over length":
comment(testfilename, "replace me", "10")
testfilename.checkFile(commentExpected)
test "comment negative":
comment(testfilename, "replace me", "-3")
testfilename.checkFile(testFileContent)
test "comment zero":
comment(testfilename, "replace me", "0")
testfilename.checkFile(testFileContent)
test "comment middle":
comment(testfilename, "is text", "2")
testfilename.checkFile(commentMiddleExpected)
############### Static inline removal ################
test "replace static inline with front braces at end of line":
let
file = dataDir / "teststaticfrontbraces.h"
resFile = dataDir / "teststaticexpectedfrontbraces.h"
test = readFile(file)
expected = readFile(resFile)
writeFile(testfilename, test)
removeStatic(testfilename)
testfilename.checkFile(expected)
reAddStatic(testfilename)
testfilename.checkFile(test)
test "replace static inline with newline before brace":
let
file = dataDir / "teststaticnewlinebraces.h"
resFile = dataDir / "teststaticexpectednewlinebraces.h"
reAddedFile = dataDir / "teststaticnewlinebracesreadded.h"
test = readFile(file)
expected = readFile(resFile)
reAdded = readFile(reAddedFile)
writeFile(testfilename, test)
removeStatic(testfilename)
testfilename.checkFile(expected)
reAddStatic(testfilename)
testfilename.checkFile(reAdded)

1
web/CNAME Normal file
View file

@ -0,0 +1 @@
nimgen.genotrance.com

1401
web/nimdoc.cfg Normal file

File diff suppressed because it is too large Load diff