├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── appveyor.yml ├── nimgen.nimble ├── src ├── nimgen.nim └── nimgen │ ├── c2nim.nim │ ├── external.nim │ ├── file.nim │ ├── fileops.nim │ ├── gencore.nim │ ├── globals.nim │ └── runcfg.nim ├── tests ├── nimgentest.nims ├── rununittests.nim └── unittests │ ├── common.nim │ ├── data │ ├── teststaticexpectedfrontbraces.h │ ├── teststaticexpectednewlinebraces.h │ ├── teststaticfrontbraces.h │ ├── teststaticnewlinebraces.h │ └── teststaticnewlinebracesreadded.h │ ├── nim.cfg │ └── testfileops.nim └── web ├── CNAME └── nimdoc.cfg /.gitignore: -------------------------------------------------------------------------------- 1 | nimcache 2 | nimgen 3 | web 4 | *.exe 5 | *.swp 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | os: 2 | - linux 3 | - osx 4 | 5 | language: c 6 | 7 | env: 8 | - BRANCH=0.20.2 9 | - BRANCH=1.0.6 10 | - BRANCH=1.2.0 11 | - BRANCH=devel 12 | 13 | cache: 14 | directories: 15 | - "$HOME/.choosenim" 16 | 17 | addons: 18 | apt: 19 | packages: 20 | - libssh2-1-dev 21 | 22 | before_install: 23 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then HOMEBREW_NO_AUTO_UPDATE=1 brew install libssh2; fi 24 | 25 | install: 26 | - curl https://gist.github.com/genotrance/fb53504a4fba88bc5201d3783df5c522/raw/travis.sh -LsSf -o travis.sh 27 | - source travis.sh 28 | 29 | script: 30 | - nimble install -y 31 | - nimble test 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Ganesh Viswanathan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Nimgen has been superseded by [nimterop](https://github.com/nimterop/nimterop). Existing wrappers are slowly being migrated over to nimterop. No new features will be implemented going forward. 2 | 3 | [![Chat on Gitter](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/nimgen/Lobby) 4 | [![Build status](https://ci.appveyor.com/api/projects/status/05t5ja88lmv1rt3r/branch/master?svg=true)](https://ci.appveyor.com/project/genotrance/nimgen/branch/master) 5 | [![Build Status](https://travis-ci.org/genotrance/nimgen.svg?branch=master)](https://travis-ci.org/genotrance/nimgen) 6 | 7 | Nimgen is a helper for [c2nim](https://github.com/nim-lang/c2nim/) to simplify and automate the wrapping of C libraries. 8 | 9 | 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. 10 | 11 | __Installation__ 12 | 13 | Nimgen can be installed via [Nimble](https://github.com/nim-lang/nimble): 14 | 15 | ```> nimble install nimgen``` 16 | 17 | This will download, build and install nimgen in the standard Nimble package location, typically ~/.nimble. Once installed, it can be run just like c2nim. 18 | 19 | __Usage__ 20 | 21 | 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. 22 | 23 | ```> nimgen package.cfg``` 24 | 25 | A Nimble package for a library that is wrapped with nimgen will have the following:- 26 | 27 | * The .cfg file that tells nimgen what exactly to do 28 | * Nimgen defined as a dependency defined in the .nimble file 29 | * Steps within the .nimble file to download the source code that is being wrapped 30 | 31 | This way, the library source code doesn't need to get checked into the Nimble package and can evolve independently. 32 | 33 | Nimble already requires Git so those commands can be assumed to be present to download source from a repository. Mercurial is also suggested but depends on the user. Downloading arbitrary files depends on the OS. For Linux, wget/curl can be assumed. On Windows, [powershell](https://superuser.com/questions/362152/native-alternative-to-wget-in-windows-powershell) can be used. 34 | 35 | __Capabilities & Limitations__ 36 | 37 | Nimgen supports compiling in C/C++ sources and static libraries as well as loading in dynamic libraries. 38 | 39 | To see examples of nimgen in action check out the following wrappers:- 40 | * Link with a dynamic library 41 | * [nimlibxlsxwriter](https://github.com/KeepCoolWithCoolidge/nimlibxlsxwriter) - libxlsxwriter wrapper 42 | * git checkout 43 | * [nim-clblast](https://github.com/numforge/nim-clblast) - OpenCL BLAS wrapper 44 | * static 45 | 46 | * Compile C/C++ code into binary 47 | * [nim7z](https://github.com/genotrance/nim7z) - 7z decoder wrapper: [docs](http://nimgen.genotrance.com/nim7z) 48 | * git sparse checkout 49 | * [nimbigwig](https://github.com/genotrance/nimbigwig) - libbigWig wrapper: [docs](http://nimgen.genotrance.com/nimbigwig) 50 | * git checkout 51 | * [nimclipboard](https://github.com/genotrance/nimclipboard) - libclipboard wrapper: [docs](http://nimgen.genotrance.com/nimclipboard) 52 | * git checkout 53 | * [nimfastText](https://github.com/genotrance/nimfastText) - fastText wrapper: [docs](http://nimgen.genotrance.com/nimfastText) 54 | * git sparse checkout 55 | * [nimfuzzy](https://github.com/genotrance/nimfuzzy) - fts_fuzzy_match wrapper: [docs](http://nimgen.genotrance.com/nimfuzzy) 56 | * download header file 57 | * [nimkerberos](https://github.com/genotrance/nimkerberos) - WinKerberos wrapper: [docs](http://nimgen.genotrance.com/nimkerberos) 58 | * git sparse checkout 59 | * [nimmonocypher](https://github.com/genotrance/nimmonocypher) - monocypher wrapper: [docs](http://nimgen.genotrance.com/nimmonocypher) 60 | * git sparse checkout 61 | * [nimnuklear](https://github.com/genotrance/nimnuklear) - nuklear wrapper: [docs](https://nimgen.genotrance.com/nimnuklear) 62 | * git sparse checkout 63 | * [nimrax](https://github.com/genotrance/nimrax) - Radix tree wrapper: [docs](http://nimgen.genotrance.com/nimrax) 64 | * git checkout 65 | * [nimtess2](https://github.com/genotrance/nimtess2) - libtess2 wrapper: [docs](http://nimgen.genotrance.com/nimtess2) 66 | * git checkout 67 | * [duktape-nim](https://github.com/manguluka/duktape-nim) - Duktape wrapper 68 | * static 69 | 70 | * Compile in as static binary 71 | * [nimssh2](https://github.com/genotrance/nimssh2) - libssh2 wrapper: [docs](http://nimgen.genotrance.com/nimssh2) 72 | * git sparse checkout 73 | 74 | Nimgen only supports the ```gcc``` preprocessor at this time. Support for detecting and using other preprocessors will be based on interest. 75 | 76 | __Config file__ 77 | 78 | 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: 79 | 80 | [n.global] 81 | c_compiler="$CC" 82 | cpp_compiler="${CPP}-arm" 83 | output="src/path" 84 | 85 | [n.include] 86 | "${output}/library/include" 87 | "${MY_INCLUDE_PATH}/include" 88 | 89 | 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. 90 | 91 | _[n.global]_ 92 | 93 | ```output``` = name of the Nimble project once installed, also location to place generated .nim files 94 | 95 | ```quotes``` = pick up any headers included using "" (and not <> which is typically used for standard headers) [default: true] 96 | 97 | ```filter``` = string to identify and recurse into library .h files in #include statements and exclude standard headers 98 | 99 | ```cpp_compiler``` = string to specify a CPP compiler executable. [default: g++] 100 | 101 | ```c_compiler``` = string to specify a C compiler executable. [default: gcc] 102 | 103 | _[n.include]_ 104 | 105 | List of all directories, one per line, to include in the search path. This is used by:- 106 | * The preprocessor for #include files 107 | * Nimgen to find #include files that are recursively processed 108 | 109 | Nimgen also adds {.passC.} declarations into the generated .nim files for these include paths if compiling source files directly. 110 | 111 | _[n.exclude]_ 112 | 113 | List of all directories or files to exclude from all parsing. If an entry here matches any portion of a file, it is excluded from recursive processing. 114 | 115 | _[n.prepare]_ 116 | 117 | 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. 118 | 119 | ```download``` = url to download to the output directory. ZIP files are automatically extracted. Files are not redownloaded if already present but re-extracted 120 | 121 | ```extract``` = ZIP file to extract in case they are local and don't need to be downloaded. Path is relative to output directory. 122 | 123 | ```gitcheckout``` = branch, commit or tag of repository to checkout in following Git command, resets after each use. Use "-b name" for branches 124 | 125 | ```gitbranch``` = master/main branch of the repository, defaults to `master` 126 | 127 | ```gitoutput``` = directory for all following Git commands relative to `n.global:output` [default: `n.global:output` directory] 128 | 129 | ```git``` = url of Git repository to clone. Full repo is pulled so gitremote + gitsparse is preferable. Resets if already present 130 | 131 | ```gitremote``` = url of Git repository to partially checkout. Use with gitsparse to pull only files and dirs of interest 132 | 133 | ```gitsparse``` = list of files and/or dirs to include in partial checkout, one per line. Resets if already present 134 | 135 | ```execute``` = command to run during preparation 136 | 137 | ```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" 138 | 139 | _[n.post]_ 140 | 141 | This section is the same as the prepare section, but for performing actions after the project has been processed. 142 | 143 | ```gitoutput``` = output directory for Git reset [default: `n.global:output` directory] 144 | 145 | ```reset``` = perform a Git reset on all files after processing [default: false] 146 | 147 | ```execute``` = command to run after processing 148 | 149 | _[n.wildcard]_ 150 | 151 | 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. 152 | 153 | ```wildcard``` = pattern to match against. All keys following the wildcard declaration will apply to matched files 154 | 155 | _[n.sourcefile]_ 156 | 157 | 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. 158 | 159 | _[sourcefile]_ 160 | 161 | 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. 162 | 163 | ```recurse``` = find #include files and process them [default: false] 164 | 165 | ```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] 166 | 167 | ```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 168 | 169 | ```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 170 | 171 | ```defines``` = pulls out simple #defines of ints, floats and hex values for separate conversion [default: false] - works only when preprocess or ctags is used and helps include useful definitions in generated .nim file 172 | 173 | ```flags``` = flags to pass to the c2nim process in "quotes" [default: --stdcall]. --cdecl, --assumedef, --assumendef may be useful 174 | 175 | ```ppflags``` = flags to pass to the preprocessor [default: ""]. -D for gcc and others may be useful 176 | 177 | ```noprocess``` = do not process this source file with c2nim [default: false] - this is useful if a file only needs to be manipulated 178 | 179 | ```nowildcard``` = ignore any wildcard definitions for this sourcefile 180 | 181 | ```reset``` = reset the file back to original state after all processing [default: false] 182 | 183 | Multiple entries for the all following keys are possible by appending any .string to the key. E.g. dynlib.win, compile.dir 184 | 185 | ```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""" 186 | 187 | ```pragma``` = pragmas to define in generated .nim file. E.g. pragma = "passL: \"-lssl\"" => {.passL: "-lssl".} 188 | 189 | ```dynlib``` = dynamic library to load at runtime for generated .nim procs 190 | 191 | 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```. 192 | 193 | ```create``` = create a file at exact location with contents specified. File needs to be in the _[n.exclude]_ list in order to be created. 194 | 195 | ```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'" 196 | 197 | ```search``` = search string providing context for following prepend/append/replace directives 198 | 199 | ```regex``` = regex search string providing context for the following replace directive. Specify using """ to avoid regex parsing issues 200 | 201 | ```prepend``` = string value to prepend into file at beginning or before search 202 | 203 | ```append``` = string value to append into file at the end or after search 204 | 205 | ```replace``` = string value to replace search string in file. Regex captures can be referred to using $1, $2, etc. 206 | 207 | ```move``` = search string providing context for location to move the results of a preceding search or regex match 208 | 209 | ```comment``` = number of lines to comment from search location 210 | 211 | 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. 212 | 213 | ```rename``` = string value to rename generated filename. E.g. rename = "$replace(7=s7)" 214 | 215 | `/` = create a directory/module hierarchy 216 | 217 | `$nimout` = refer to the original filename 218 | 219 | `$replace(srch1=repl1, srch2=reply2)` = rename specific portions in `$nimout` 220 | 221 | __Command Line__ 222 | 223 | 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. 224 | 225 | ``` 226 | Usage: 227 | nimgen [options] file.cfg|file.h ... 228 | 229 | Params: 230 | -C add compile entry * 231 | -E add n.exclude entry * 232 | -F set c2nim flags * 233 | -I add n.include dir * 234 | -O set output directory 235 | -P set preprocessor flags * 236 | 237 | Options: 238 | -c set ctags = true 239 | -d set defines = true 240 | -i set inline = true 241 | -n set noprocess = true 242 | -p set preprocess = true 243 | -r set recurse = true 244 | 245 | Editing: 246 | -a append string * 247 | -e prepend string * 248 | -l replace string * 249 | -o#lines comment X lines * 250 | -s search string * 251 | -x regex search string * 252 | 253 | * supports multiple instances 254 | ``` 255 | 256 | __Feedback__ 257 | 258 | 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. 259 | 260 | _Credits_ 261 | 262 | Thank you to the following contributors for their hard work! 263 | 264 | https://github.com/jyapayne 265 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: '{build}' 2 | 3 | image: 4 | - Ubuntu 5 | - Visual Studio 2017 6 | 7 | matrix: 8 | fast_finish: true 9 | 10 | environment: 11 | matrix: 12 | - NIM_VERSION: 1.2.0 13 | - NIM_VERSION: 1.0.6 14 | - NIM_VERSION: 0.20.2 15 | - NIM_VERSION: 0.19.6 16 | 17 | for: 18 | - 19 | matrix: 20 | only: 21 | - image: Visual Studio 2017 22 | 23 | environment: 24 | ARCH: 32 25 | MINGW_URL: https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win32/Personal%20Builds/mingw-builds/8.1.0/threads-posix/dwarf 26 | MINGW_ARCHIVE: i686-8.1.0-release-posix-dwarf-rt_v6-rev0.7z 27 | SFNET_URL: https://sourceforge.net/projects/msys2/files/REPOS/MINGW/i686 28 | LIBSSH2_ARCHIVE: mingw-w64-i686-libssh2-1.8.0-1-any.pkg 29 | LIBCRYPTO_ARCHIVE: mingw-w64-i686-openssl-1.0.2.o-1-any.pkg 30 | 31 | install: 32 | - CD c:\ 33 | - IF not exist "binaries" ( 34 | echo %NIM_VERSION% && 35 | MKDIR binaries && 36 | CD binaries && 37 | appveyor DownloadFile "%MINGW_URL%/%MINGW_ARCHIVE%/download" -FileName "%MINGW_ARCHIVE%" && 38 | 7z x -y "%MINGW_ARCHIVE%"> nul && 39 | del "%MINGW_ARCHIVE%" && 40 | appveyor DownloadFile "%SFNET_URL%/%LIBSSH2_ARCHIVE%.tar.xz/download" -FileName "%LIBSSH2_ARCHIVE%.tar.xz" && 41 | 7z x -y "%LIBSSH2_ARCHIVE%.tar.xz"> nul && 42 | del "%LIBSSH2_ARCHIVE%.tar.xz" && 43 | 7z x -y "%LIBSSH2_ARCHIVE%.tar"> nul && 44 | del "%LIBSSH2_ARCHIVE%.tar" && 45 | appveyor DownloadFile "%SFNET_URL%/%LIBCRYPTO_ARCHIVE%.tar.xz/download" -FileName "%LIBCRYPTO_ARCHIVE%.tar.xz" && 46 | 7z x -y "%LIBCRYPTO_ARCHIVE%.tar.xz"> nul && 47 | del "%LIBCRYPTO_ARCHIVE%.tar.xz" && 48 | 7z x -y "%LIBCRYPTO_ARCHIVE%.tar"> nul && 49 | del "%LIBCRYPTO_ARCHIVE%.tar" && 50 | appveyor DownloadFile "https://nim-lang.org/download/nim-%NIM_VERSION%_x%ARCH%.zip" -FileName "nim-%NIM_VERSION%_x%ARCH%.zip" && 51 | 7z x -y "nim-%NIM_VERSION%_x%ARCH%.zip"> nul && 52 | del "nim-%NIM_VERSION%_x%ARCH%.zip") 53 | - SET PATH=c:\binaries\mingw%ARCH%\bin;c:\binaries\nim-%NIM_VERSION%\bin;%USERPROFILE%\.nimble\bin;%PATH% 54 | - CD c:\projects\nimgen 55 | 56 | on_finish: 57 | - 7z a -r buildlogs-win-pkgs.zip %USERPROFILE%\.nimble\pkgs 58 | - appveyor PushArtifact buildlogs-win-pkgs.zip 59 | - 7z a -r buildlogs-win-projects.zip c:\projects\* 60 | - appveyor PushArtifact buildlogs-win-projects.zip 61 | - 7z a -r nimgen-docs.zip c:\projects\nimgen\web 62 | - appveyor PushArtifact nimgen-docs.zip 63 | 64 | cache: 65 | - c:\binaries 66 | 67 | - 68 | matrix: 69 | only: 70 | - image: Ubuntu 71 | 72 | install: 73 | - sudo apt -qq update 74 | - sudo apt -qq install --yes libssh2-1-dev libgcrypt20-dev libgpg-error-dev 75 | - if [ ! -e /home/appveyor/binaries ]; then 76 | echo $NIM_VERSION && 77 | mkdir /home/appveyor/binaries && 78 | cd /home/appveyor/binaries && 79 | curl -s -o nim-$NIM_VERSION.tar.xz https://nim-lang.org/download/nim-$NIM_VERSION.tar.xz && 80 | tar xJf nim-$NIM_VERSION.tar.xz && 81 | cd nim-$NIM_VERSION && 82 | sh build.sh && 83 | bin/nim c -d:release koch && 84 | ./koch boot -d:release && 85 | ./koch nimble -d:release; 86 | fi 87 | - export PATH=/home/appveyor/binaries/nim-$NIM_VERSION/bin:~/.nimble/bin:$PATH 88 | - cd /home/appveyor/projects/nimgen 89 | 90 | on_finish: 91 | - zip -r -q buildlogs-lin-pkgs.zip ~/.nimble/pkgs 92 | - appveyor PushArtifact buildlogs-lin-pkgs.zip 93 | - zip -r -q buildlogs-lin-projects.zip /home/appveyor/projects 94 | - appveyor PushArtifact buildlogs-lin-projects.zip 95 | 96 | cache: 97 | - /home/appveyor/binaries 98 | 99 | build_script: 100 | - nimble install -y 101 | 102 | test_script: 103 | - nimble test 104 | 105 | deploy: off 106 | -------------------------------------------------------------------------------- /nimgen.nimble: -------------------------------------------------------------------------------- 1 | # Package 2 | 3 | version = "0.5.3" 4 | author = "genotrance" 5 | description = "c2nim helper to simplify and automate the wrapping of C libraries" 6 | license = "MIT" 7 | 8 | bin = @["nimgen"] 9 | srcDir = "src" 10 | skipDirs = @["nimgen", "tests", "web"] 11 | 12 | # Dependencies 13 | 14 | requires "nim >= 0.19.0", "c2nim >= 0.9.14", "regex >= 0.12.0" 15 | 16 | task test, "Test nimgen": 17 | exec "nim c -r tests/rununittests.nim" 18 | exec "nim e tests/nimgentest.nims" 19 | -------------------------------------------------------------------------------- /src/nimgen.nim: -------------------------------------------------------------------------------- 1 | import nimgen/runcfg 2 | 3 | runCli() 4 | -------------------------------------------------------------------------------- /src/nimgen/c2nim.nim: -------------------------------------------------------------------------------- 1 | import os, regex, strutils 2 | 3 | when (NimMajor, NimMinor, NimPatch) < (0, 19, 9): 4 | import ospaths 5 | 6 | import external, file, fileops, gencore, globals 7 | 8 | const passCBase = """ 9 | 10 | import os, strutils 11 | # import std/time_t # To use C "time_t" uncomment this line and use time_t.Time 12 | 13 | const sourcePath = currentSourcePath().splitPath.head 14 | """ 15 | 16 | proc relativePath(path: string): string = 17 | if gOutput.len() == 0: 18 | result = path 19 | else: 20 | # multiReplace() bug - #9557 21 | result = path.replace(gOutput, "") 22 | return result.multiReplace([("\\", "/"), ("//", "/")]) 23 | 24 | proc c2nim*(fl, outfile: string, c2nimConfig: c2nimConfigObj) = 25 | var file = search(fl) 26 | if file.len() == 0: 27 | return 28 | 29 | echo "Generating " & outfile 30 | 31 | var cfile = file 32 | if c2nimConfig.preprocess: 33 | cfile = "temp-$#.c" % [outfile.extractFilename()] 34 | writeFileFlush(cfile, runPreprocess(file, c2nimConfig.ppflags, c2nimConfig.flags, c2nimConfig.inline)) 35 | elif c2nimConfig.ctags: 36 | cfile = "temp-$#.c" % [outfile.extractFilename()] 37 | writeFileFlush(cfile, runCtags(file)) 38 | 39 | if c2nimConfig.defines and (c2nimConfig.preprocess or c2nimConfig.ctags): 40 | prepend(cfile, getDefines(file, c2nimConfig.inline)) 41 | 42 | var 43 | extflags = "" 44 | passC = "# " & file & " --> " & outfile & passCBase 45 | outlib = "" 46 | outpragma = "" 47 | 48 | for inc in gIncludes: 49 | if inc.isAbsolute(): 50 | passC &= ("""{.passC: "-I\"$#\"".}""" % [inc.sanitizePath()]) & "\n" 51 | else: 52 | passC &= ( 53 | """{.passC: "-I\"" & sourcePath & "$#\"".}""" % 54 | inc.relativePath() 55 | ) & "\n" 56 | 57 | for prag in c2nimConfig.pragma: 58 | outpragma &= "{." & prag & ".}\n" 59 | 60 | let fname = file.splitFile.name.multiReplace([(".", "_"), ("-", "_")]).normalize.capitalizeAscii 61 | 62 | if c2nimConfig.dynlib.len() != 0: 63 | let 64 | win = "when defined(Windows):\n" 65 | lin = "when defined(Linux):\n" 66 | osx = "when defined(MacOSX):\n" 67 | 68 | var winlib, linlib, osxlib: string = "" 69 | for dl in c2nimConfig.dynlib: 70 | let 71 | lib = " const dynlib$# = \"$#\"\n" % [fname, dl] 72 | ext = dl.splitFile().ext 73 | 74 | if ext == ".dll": 75 | winlib &= lib 76 | elif ext == ".so": 77 | linlib &= lib 78 | elif ext == ".dylib": 79 | osxlib &= lib 80 | 81 | if winlib != "": 82 | outlib &= win & winlib & "\n" 83 | if linlib != "": 84 | outlib &= lin & linlib & "\n" 85 | if osxlib != "": 86 | outlib &= osx & osxlib & "\n" 87 | 88 | if outlib != "": 89 | extflags &= " --dynlib:dynlib$#" % fname 90 | else: 91 | if file.isAbsolute(): 92 | passC &= "const header$# = \"$#\"\n" % [fname, file] 93 | else: 94 | passC &= "const header$# = sourcePath / \"$#\"\n" % 95 | [fname, file.relativePath()] 96 | extflags = "--header:header$#" % fname 97 | # Run c2nim on generated file 98 | var cmd = "c2nim $# $# --out:$# $#" % [c2nimConfig.flags, extflags, outfile, cfile] 99 | when defined(windows): 100 | cmd = "cmd /c " & cmd.quoteShell 101 | discard execProc(cmd) 102 | 103 | if c2nimConfig.preprocess or c2nimConfig.ctags: 104 | try: 105 | removeFile(cfile) 106 | except: 107 | discard 108 | 109 | # Nim doesn't like {.cdecl.} for type proc() 110 | freplace(outfile, re"(?m)(.*? = proc.*?)\{.cdecl.\}", "$1") 111 | freplace(outfile, " {.cdecl.})", ")") 112 | 113 | # Include {.compile.} directives 114 | for cpl in c2nimConfig.compile: 115 | prepend(outfile, compile(cpl, c2nimConfig.flags)) 116 | 117 | # Add any pragmas 118 | if outpragma != "": 119 | prepend(outfile, outpragma) 120 | 121 | # Add header file and include paths 122 | if passC != "": 123 | prepend(outfile, passC) 124 | 125 | # Add dynamic library 126 | if outlib != "": 127 | prepend(outfile, outlib) 128 | -------------------------------------------------------------------------------- /src/nimgen/external.nim: -------------------------------------------------------------------------------- 1 | import os, osproc, regex, strutils 2 | 3 | import globals 4 | 5 | proc sanitizePath*(path: string): string = 6 | path.multiReplace([("\\", "/"), ("//", "/")]) 7 | 8 | proc execProc*(cmd: string): string = 9 | var ret: int 10 | 11 | (result, ret) = execCmdEx(cmd) 12 | if ret != 0: 13 | echo "Command failed: " & $ret 14 | echo cmd 15 | echo result 16 | quit(1) 17 | 18 | proc execAction*(cmd: string): string = 19 | var ccmd = "" 20 | when defined(Windows): 21 | ccmd = "cmd /c " & cmd.replace("/", "\\") 22 | when defined(Linux) or defined(MacOSX): 23 | ccmd = "bash -c '" & cmd & "'" 24 | 25 | echo "Running '" & ccmd[0..min(64, len(ccmd)-1)] & "'" 26 | return execProc(ccmd) 27 | 28 | proc extractZip*(zipfile: string) = 29 | var cmd = "unzip -o $#" 30 | if defined(Windows): 31 | cmd = "powershell -nologo -noprofile -command \"& { Add-Type -A " & 32 | "'System.IO.Compression.FileSystem'; " & 33 | "[IO.Compression.ZipFile]::ExtractToDirectory('$#', '.'); }\"" 34 | 35 | setCurrentDir(gOutput) 36 | defer: setCurrentDir(gProjectDir) 37 | 38 | echo "Extracting " & zipfile 39 | discard execProc(cmd % zipfile) 40 | 41 | proc downloadUrl*(url: string) = 42 | let 43 | file = url.extractFilename() 44 | ext = file.splitFile().ext.toLowerAscii() 45 | 46 | var cmd = if defined(Windows): 47 | "powershell [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; wget $# -OutFile $#" 48 | else: 49 | "curl -Lk $# -o $#" 50 | 51 | if not (ext == ".zip" and fileExists(gOutput/file)): 52 | echo "Downloading " & file 53 | discard execProc(cmd % [url, gOutput/file]) 54 | 55 | if ext == ".zip": 56 | extractZip(file) 57 | 58 | template setGitDir() = 59 | setCurrentDir(gGitOutput) 60 | defer: setCurrentDir(gProjectDir) 61 | 62 | proc gitReset*() = 63 | echo "Resetting " & gGitOutput 64 | 65 | setGitDir() 66 | 67 | let cmd = "git reset --hard" 68 | while execCmdEx(cmd)[0].contains("Permission denied"): 69 | sleep(1000) 70 | echo " Retrying ..." 71 | 72 | proc gitCheckout*(file: string) = 73 | echo "Resetting " & file 74 | 75 | setGitDir() 76 | 77 | let cmd = "git checkout $#" % file.replace(gGitOutput & "/", "") 78 | while execCmdEx(cmd)[0].contains("Permission denied"): 79 | sleep(500) 80 | echo " Retrying ..." 81 | 82 | proc gitPull() = 83 | let branch = if gGitBranch != "": gGitBranch else: "master" 84 | if gGitCheckout.len() != 0: 85 | echo "Checking out " & gGitCheckout 86 | discard execProc("git pull --tags origin " & branch) 87 | discard execProc("git checkout " & gGitCheckout) 88 | gGitCheckout = "" 89 | else: 90 | echo "Pulling repository" 91 | discard execProc("git pull --depth=1 origin " & branch) 92 | 93 | proc gitRemotePull*(url: string, pull=true) = 94 | if dirExists(gGitOutput/".git"): 95 | if pull: 96 | gitReset() 97 | return 98 | 99 | setGitDir() 100 | 101 | echo "Setting up Git repo: " & url 102 | discard execProc("git init .") 103 | discard execProc("git remote add origin " & url) 104 | 105 | if pull: 106 | gitPull() 107 | 108 | proc gitSparseCheckout*(plist: string) = 109 | let sparsefile = ".git/info/sparse-checkout" 110 | if fileExists(gGitOutput/sparsefile): 111 | gitReset() 112 | return 113 | 114 | setGitDir() 115 | 116 | discard execProc("git config core.sparsecheckout true") 117 | writeFile(sparsefile, plist) 118 | 119 | gitPull() 120 | 121 | proc runPreprocess*(file, ppflags, flags: string, inline: bool): string = 122 | var 123 | pproc = if flags.contains("cpp"): gCppCompiler else: gCCompiler 124 | cmd = "$# -E $# $#" % [pproc, ppflags, file] 125 | 126 | for inc in gIncludes: 127 | cmd &= " -I " & inc.quoteShell 128 | 129 | # Run preprocessor 130 | var data = execProc(cmd) 131 | 132 | # Include content only from file 133 | var 134 | rdata: seq[string] = @[] 135 | start = false 136 | sfile = file.sanitizePath 137 | 138 | if inline: 139 | sfile = sfile.parentDir() 140 | for line in data.splitLines(): 141 | if line.strip() != "": 142 | if line[0] == '#' and not line.contains("#pragma"): 143 | start = false 144 | if sfile in line.sanitizePath: 145 | start = true 146 | if not ("\\" in line) and not ("/" in line) and extractFilename(sfile) in line: 147 | start = true 148 | else: 149 | if start: 150 | rdata.add( 151 | line.multiReplace([("_Noreturn", ""), ("(())", ""), ("WINAPI", ""), 152 | ("__attribute__", ""), ("extern \"C\"", "")]) 153 | .replace(re"\(\([_a-z]+?\)\)", "") 154 | .replace(re"\(\(__format__[\s]*\(__[gnu_]*printf__, [\d]+, [\d]+\)\)\);", ";") 155 | ) 156 | return rdata.join("\n") 157 | 158 | proc runCtags*(file: string): string = 159 | var 160 | cmd = "ctags -o - --fields=+S+K --c-kinds=+p --file-scope=no " & file 161 | fps = execProc(cmd) 162 | fdata = "" 163 | 164 | for line in fps.splitLines(): 165 | var spl = line.split('\t') 166 | if spl.len() > 4: 167 | if spl[0] != "main" and spl[3] != "member": 168 | var fn = "" 169 | var match: RegexMatch 170 | if spl[2].find(re"/\^(.*?)\(", match): 171 | fn = spl[2][match.group(0)[0]] 172 | fn &= spl[4].replace("signature:", "") & ";" 173 | fdata &= fn & "\n" 174 | 175 | return fdata 176 | -------------------------------------------------------------------------------- /src/nimgen/file.nim: -------------------------------------------------------------------------------- 1 | import os, pegs, strutils, tables 2 | 3 | when (NimMajor, NimMinor, NimPatch) < (0, 19, 9): 4 | import ospaths 5 | 6 | import globals, external 7 | 8 | # ### 9 | # File loction 10 | 11 | proc getNimout*(file: string, rename=true): string = 12 | result = file.splitFile().name.multiReplace([("-", "_"), (".", "_")]) & ".nim" 13 | if gOutput != "": 14 | result = gOutput & "/" & result 15 | 16 | if not rename: 17 | return 18 | 19 | if gRenames.hasKey(file): 20 | result = gRenames[file] 21 | 22 | createDir(parentDir(result)) 23 | 24 | proc exclude*(file: string): bool = 25 | for excl in gExcludes: 26 | if excl in file: 27 | return true 28 | return false 29 | 30 | proc search*(file: string): string = 31 | if exclude(file): 32 | return "" 33 | 34 | result = file 35 | if file.splitFile().ext == ".nim": 36 | result = getNimout(file) 37 | elif not fileExists(result) and not dirExists(result): 38 | var found = false 39 | for inc in gIncludes: 40 | result = inc & "/" & file 41 | if fileExists(result) or dirExists(result): 42 | found = true 43 | break 44 | if not found: 45 | echo "File doesn't exist: " & file 46 | quit(1) 47 | 48 | # Only keep relative directory 49 | return result.sanitizePath.replace(gProjectDir & "/", "") 50 | 51 | proc rename*(file: string, renfile: string) = 52 | if file.splitFile().ext == ".nim": 53 | return 54 | 55 | var 56 | nimout = getNimout(file, false) 57 | newname = renfile.replace("$nimout", extractFilename(nimout)) 58 | 59 | if newname =~ peg"(!\$.)*{'$replace'\s*'('\s*{(!\)\S)+}')'}": 60 | var final = nimout.extractFilename() 61 | for entry in matches[1].split(","): 62 | let spl = entry.split("=") 63 | if spl.len() != 2: 64 | echo "Bad replace syntax: " & renfile 65 | quit(1) 66 | 67 | let 68 | srch = spl[0].strip() 69 | repl = spl[1].strip() 70 | 71 | final = final.replace(srch, repl) 72 | newname = newname.replace(matches[0], final) 73 | 74 | gRenames[file] = gOutput & "/" & newname 75 | 76 | # ### 77 | # Actions 78 | 79 | proc openRetry*(file: string, mode: FileMode = fmRead): File = 80 | while true: 81 | try: 82 | result = open(file, mode) 83 | break 84 | except IOError: 85 | sleep(100) 86 | 87 | template writeFileFlush*(file, content: string): untyped = 88 | let f = openRetry(file, fmWrite) 89 | f.write(content) 90 | f.flushFile() 91 | f.close() 92 | 93 | template withFile*(file: string, body: untyped): untyped = 94 | if fileExists(file): 95 | var f = openRetry(file) 96 | 97 | var contentOrig = f.readAll() 98 | f.close() 99 | var content {.inject.} = contentOrig 100 | 101 | body 102 | 103 | if content != contentOrig: 104 | writeFileFlush(file, content) 105 | else: 106 | echo "Missing file " & file 107 | 108 | proc doCopy*(flist: string) = 109 | for pair in flist.split(","): 110 | let spl = pair.split("=") 111 | if spl.len() != 2: 112 | echo "Bad copy syntax: " & flist 113 | quit(1) 114 | 115 | let 116 | lfile = spl[0].strip() 117 | rfile = spl[1].strip() 118 | 119 | copyFile(lfile, rfile) 120 | echo "Copied $# to $#" % [lfile, rfile] 121 | -------------------------------------------------------------------------------- /src/nimgen/fileops.nim: -------------------------------------------------------------------------------- 1 | import os, regex, strutils 2 | 3 | import external, file 4 | 5 | # ### 6 | # Manipulating content 7 | 8 | proc prepend*(file: string, data: string, search="") = 9 | withFile(file): 10 | if search == "": 11 | content = data & content 12 | else: 13 | let idx = content.find(search) 14 | if idx != -1: 15 | content = content[0..= content.len(): 71 | break 72 | content = content[0.."]""", "").replace(re"\/[\*\/].*$", "").strip() 68 | try: 69 | # Try searching for a local library. expandFilename will throw 70 | # OSError if the file does not exist 71 | let 72 | finc = expandFileName(curPath / addInc) 73 | fname = finc.replace(curPath & $DirSep, "") 74 | 75 | if fname.len() > 0: 76 | # only add if the file is non-empty 77 | result.add(fname.search()) 78 | except OSError: 79 | # If it's a system library 80 | result.add(addInc) 81 | 82 | result = result.deduplicate() 83 | 84 | gDoneInline.add(file) 85 | 86 | if inline: 87 | var sres = newSeq[string]() 88 | for incl in result: 89 | let sincl = search(incl) 90 | if sincl == "": 91 | continue 92 | 93 | sres.add(getIncls(sincl, inline)) 94 | result.add(sres) 95 | 96 | result = result.deduplicate() 97 | 98 | proc getDefines*(file: string, inline=false): string = 99 | result = "" 100 | if inline: 101 | var incls = getIncls(file, inline) 102 | for incl in incls: 103 | let sincl = search(incl) 104 | if sincl != "": 105 | result &= getDefines(sincl) 106 | withFile(file): 107 | for def in content.findAll(re"(?m)^(\s*#\s*define\s+[\w\d_]+\s+[\d\-.xf]+)(?:\r|//|/*).*?$"): 108 | result &= content[def.group(0)[0]] & "\n" 109 | -------------------------------------------------------------------------------- /src/nimgen/globals.nim: -------------------------------------------------------------------------------- 1 | import os, parsecfg, tables 2 | 3 | const 4 | cCompilerEnv* = "CC" 5 | cppCompilerEnv* = "CPP" 6 | defaultCCompiler* = "gcc" 7 | defaultCppCompiler* = "g++" 8 | 9 | var 10 | # Config 11 | gConfig*: Config 12 | gExcludes*: seq[string] = @[] 13 | gIncludes*: seq[string] = @[] 14 | gRenames* = initTable[string, string]() 15 | gWildcards* = newConfig() 16 | 17 | # n.global 18 | gOutput* = "." 19 | gQuotes* = true 20 | gFilter* = "" 21 | gCppCompiler* = getEnv(cppCompilerEnv, defaultCCompiler) 22 | gCCompiler* = getEnv(cCompilerEnv, defaultCppCompiler) 23 | 24 | # State tracking 25 | gGitCheckout* = "" 26 | gGitBranch* = "" 27 | gGitOutput* = "" 28 | gProjectDir* = "" 29 | gCompile*: seq[string] = @[] 30 | gDoneInline*: seq[string] = @[] 31 | gDoneRecursive*: seq[string] = @[] 32 | 33 | type 34 | c2nimConfigObj* = object 35 | flags*, ppflags*: string 36 | recurse*, inline*, preprocess*, ctags*, defines*: bool 37 | dynlib*, compile*, pragma*: seq[string] 38 | 39 | const gDoc* = """ 40 | Nimgen is a helper for c2nim to simpilfy and automate the wrapping of C libraries 41 | 42 | Usage: 43 | nimgen [options] ... 44 | 45 | Options: 46 | -f delete all artifacts and regenerate 47 | """ 48 | 49 | -------------------------------------------------------------------------------- /src/nimgen/runcfg.nim: -------------------------------------------------------------------------------- 1 | import os, parsecfg, regex, strutils, tables 2 | 3 | import c2nim, external, file, fileops, gencore, globals 4 | 5 | proc `[]`*(table: OrderedTableRef[string, string], key: string): string = 6 | ## Gets table values with env vars inserted 7 | tables.`[]`(table, key).addEnv 8 | 9 | proc getKey(ukey: string, section = false): tuple[key: string, val: bool] = 10 | var kv = if not section: ukey.replace(re"\..*", "").split("-", 1) else: ukey.split("-", 1) 11 | if kv.len() == 1: 12 | kv.add("") 13 | 14 | if kv[1] == "": 15 | return (kv[0], true) 16 | 17 | for ostyp in kv[1].split("-"): 18 | if (ostyp == "win" and defined(Windows)) or 19 | (ostyp == "lin" and defined(Linux)) or 20 | ((ostyp == "osx" or ostyp == "mac") and defined(MacOSX)) or 21 | (ostyp == "unix" and (defined(Linux) or defined(MacOSX))): 22 | return (kv[0], true) 23 | 24 | return (kv[0], false) 25 | 26 | proc runFile*(file: string, cfgin: OrderedTableRef = newOrderedTable[string, string]()) = 27 | var 28 | cfg = cfgin 29 | sfile = search(file) 30 | nowildcard = false 31 | 32 | if sfile in gDoneRecursive: 33 | return 34 | 35 | if sfile.len() != 0: 36 | echo "Processing " & sfile 37 | gDoneRecursive.add(sfile) 38 | 39 | for act in cfg.keys(): 40 | let (action, val) = getKey(act) 41 | if val == true and action == "nowildcard" and cfg[act] == "true": 42 | nowildcard = true 43 | break 44 | 45 | if not nowildcard: 46 | for pattern in gWildcards.keys(): 47 | var m: RegexMatch 48 | let pat = pattern.replace(".", "\\.").replace("*", ".*").replace("?", ".?") 49 | if file.find(toPattern(pat), m): 50 | echo " Appending keys for wildcard " & pattern 51 | for key in gWildcards[pattern].keys(): 52 | cfg[key & "." & pattern] = gWildcards[pattern][key] 53 | 54 | var 55 | srch = "" 56 | rgx = "" 57 | 58 | c2nimConfig = c2nimConfigObj( 59 | flags: "--stdcall", ppflags: "", 60 | recurse: false, inline: false, preprocess: false, ctags: false, defines: false, 61 | dynlib: @[], compile: @[], pragma: @[] 62 | ) 63 | 64 | for act in cfg.keys(): 65 | let (action, val) = getKey(act) 66 | if val == true: 67 | if action == "create": 68 | echo "Creating " & file 69 | createDir(file.splitPath().head) 70 | writeFileFlush(file, cfg[act]) 71 | if file in gExcludes: 72 | gExcludes.delete(gExcludes.find(file)) 73 | sfile = file 74 | gDoneRecursive.add(sfile) 75 | elif action in @["prepend", "append", "replace", "move", "comment", 76 | "rename", "compile", "dynlib", "pragma", "pipe"] and 77 | sfile != "": 78 | if action == "prepend": 79 | if srch != "": 80 | prepend(sfile, cfg[act], cfg[srch]) 81 | else: 82 | prepend(sfile, cfg[act]) 83 | elif action == "append": 84 | if srch != "": 85 | append(sfile, cfg[act], cfg[srch]) 86 | else: 87 | append(sfile, cfg[act]) 88 | elif action == "replace": 89 | if srch != "": 90 | freplace(sfile, cfg[srch], cfg[act]) 91 | elif rgx != "": 92 | freplace(sfile, toPattern(cfg[rgx]), cfg[act]) 93 | elif action == "move": 94 | if srch != "": 95 | move(sfile, cfg[srch], cfg[act]) 96 | elif rgx != "": 97 | move(sfile, toPattern(cfg[rgx]), cfg[act]) 98 | elif action == "comment": 99 | if srch != "": 100 | comment(sfile, cfg[srch], cfg[act]) 101 | elif action == "rename": 102 | rename(sfile, cfg[act]) 103 | elif action == "compile": 104 | c2nimConfig.compile.add(cfg[act]) 105 | elif action == "dynlib": 106 | c2nimConfig.dynlib.add(cfg[act]) 107 | elif action == "pragma": 108 | c2nimConfig.pragma.add(cfg[act]) 109 | elif action == "pipe": 110 | pipe(sfile, cfg[act]) 111 | srch = "" 112 | rgx = "" 113 | elif action == "search": 114 | srch = act 115 | elif action == "regex": 116 | rgx = act 117 | 118 | if file.splitFile().ext != ".nim": 119 | var 120 | noprocess = false 121 | reset = false 122 | 123 | for act in cfg.keys(): 124 | let (action, val) = getKey(act) 125 | if val == true: 126 | if cfg[act] == "true": 127 | if action == "recurse": 128 | c2nimConfig.recurse = true 129 | elif action == "inline": 130 | c2nimConfig.inline = true 131 | elif action == "preprocess": 132 | c2nimConfig.preprocess = true 133 | elif action == "ctags": 134 | c2nimConfig.ctags = true 135 | elif action == "defines": 136 | c2nimConfig.defines = true 137 | elif action == "noprocess": 138 | noprocess = true 139 | elif action == "reset": 140 | reset = true 141 | elif action == "flags": 142 | c2nimConfig.flags = cfg[act] 143 | elif action == "ppflags": 144 | c2nimConfig.ppflags = cfg[act] 145 | 146 | if c2nimConfig.recurse and c2nimConfig.inline: 147 | echo "Cannot use recurse and inline simultaneously" 148 | quit(1) 149 | 150 | removeStatic(sfile) 151 | fixFuncProtos(sfile) 152 | 153 | let outfile = getNimout(sfile) 154 | var incout = "" 155 | if c2nimConfig.recurse or c2nimConfig.inline: 156 | var 157 | cfg = newOrderedTable[string, string]() 158 | incls = getIncls(sfile) 159 | 160 | for name, value in c2nimConfig.fieldPairs: 161 | when value is string: 162 | cfg[name] = value 163 | when value is bool: 164 | cfg[name] = $value 165 | 166 | for i in c2nimConfig.dynlib: 167 | cfg["dynlib." & i] = i 168 | 169 | if c2nimConfig.inline: 170 | cfg["noprocess"] = "true" 171 | 172 | for inc in incls: 173 | runFile(inc, cfg) 174 | if c2nimConfig.recurse: 175 | incout &= "import $#\n" % inc.search().getNimout()[0 .. ^5] 176 | 177 | if not noprocess: 178 | c2nim(file, outfile, c2nimConfig) 179 | 180 | if c2nimConfig.recurse and incout.len() != 0: 181 | prepend(outfile, incout) 182 | 183 | if reset: 184 | gitCheckout(sfile) 185 | 186 | proc setOutputDir(dir: string) = 187 | gOutput = dir.sanitizePath 188 | if dirExists(gOutput): 189 | if "-f" in commandLineParams(): 190 | try: 191 | removeDir(gOutput) 192 | except OSError: 193 | echo "Directory in use: " & gOutput 194 | quit(1) 195 | else: 196 | for f in walkFiles(gOutput/"*.nim"): 197 | try: 198 | removeFile(f) 199 | except OSError: 200 | echo "Unable to delete: " & f 201 | quit(1) 202 | createDir(gOutput) 203 | 204 | gGitOutput = gOutput 205 | 206 | proc runCfg*(cfg: string) = 207 | if not fileExists(cfg): 208 | echo "Config doesn't exist: " & cfg 209 | quit(1) 210 | 211 | gProjectDir = parentDir(cfg.expandFilename()).sanitizePath 212 | 213 | gConfig = loadConfig(cfg) 214 | gCppCompiler = getEnv(cppCompilerEnv, defaultCppCompiler).quoteShell 215 | gCCompiler = getEnv(cCompilerEnv, defaultCCompiler).quoteShell 216 | gGitOutput = gOutput 217 | 218 | for section in gConfig.keys(): 219 | let (sname, sval) = getKey(section, true) 220 | if not sval: 221 | continue 222 | 223 | case sname: 224 | of "n.global": 225 | for glob in gConfig[section].keys(): 226 | let (key, val) = getKey(glob) 227 | if val == true: 228 | let globVal = gConfig[section][glob] 229 | case key: 230 | of "output": 231 | setOutputDir(globVal) 232 | of "cpp_compiler": 233 | gCppCompiler = globVal.quoteShell 234 | of "c_compiler": 235 | gCCompiler = globVal.quoteShell 236 | of "filter": 237 | gFilter = globVal 238 | of "quotes": 239 | if globVal == "false": 240 | gQuotes = false 241 | 242 | of "n.include": 243 | for inc in gConfig[section].keys(): 244 | gIncludes.add(inc.addEnv().sanitizePath) 245 | 246 | of "n.exclude": 247 | for excl in gConfig[section].keys(): 248 | gExcludes.add(excl.addEnv().sanitizePath) 249 | 250 | of "n.prepare": 251 | for prep in gConfig[section].keys(): 252 | let (key, val) = getKey(prep) 253 | if val == true: 254 | let prepVal = gConfig[section][prep] 255 | case key: 256 | of "download": 257 | downloadUrl(prepVal) 258 | of "extract": 259 | extractZip(prepVal) 260 | of "gitcheckout": 261 | gGitCheckout = prepVal 262 | of "gitbranch": 263 | gGitBranch = prepVal 264 | of "gitoutput": 265 | gGitOutput = gOutput/prepVal 266 | createDir(gGitOutput) 267 | of "git": 268 | gitRemotePull(prepVal) 269 | of "gitremote": 270 | gitRemotePull(prepVal, false) 271 | of "gitsparse": 272 | gitSparseCheckout(prepVal) 273 | of "execute": 274 | discard execAction(prepVal) 275 | of "copy": 276 | doCopy(prepVal) 277 | 278 | of "n.wildcard": 279 | var wildcard = "" 280 | for wild in gConfig[section].keys(): 281 | let (key, val) = getKey(wild) 282 | if val == true: 283 | if key == "wildcard": 284 | wildcard = gConfig[section][wild] 285 | else: 286 | gWildcards.setSectionKey(wildcard, wild, 287 | gConfig[section][wild]) 288 | 289 | of "n.sourcefile": 290 | for pattern in gConfig[section].keys(): 291 | for file in walkFiles(pattern.addEnv): 292 | runFile(file) 293 | 294 | of "n.post": 295 | for post in gConfig[section].keys(): 296 | let (key, val) = getKey(post) 297 | if val == true: 298 | let postVal = gConfig[section][post] 299 | case key: 300 | of "gitoutput": 301 | gGitOutput = gOutput/postVal 302 | of "reset": 303 | gitReset() 304 | of "execute": 305 | discard execAction(postVal) 306 | 307 | else: 308 | runFile(section, gConfig[section]) 309 | 310 | let gHelp = """ 311 | Nimgen is a helper for c2nim to simplify and automate the wrapping of C libraries 312 | 313 | Usage: 314 | nimgen [options] file.cfg|file.h ... 315 | 316 | Params: 317 | -C add compile entry * 318 | -E add n.exclude entry * 319 | -F set c2nim flags * 320 | -I add n.include dir * 321 | -O set output directory 322 | -P set preprocessor flags * 323 | 324 | Options: 325 | -c set ctags = true 326 | -d set defines = true 327 | -i set inline = true 328 | -n set noprocess = true 329 | -p set preprocess = true 330 | -r set recurse = true 331 | 332 | Editing: 333 | -a append string * 334 | -e prepend string * 335 | -l replace string * 336 | -o#lines comment X lines * 337 | -s search string * 338 | -x regex search string * 339 | 340 | * supports multiple instances 341 | """ 342 | 343 | proc runCli*() = 344 | var 345 | cfg = newOrderedTable[string, string]() 346 | files: seq[string] 347 | uniq = 1 348 | 349 | gProjectDir = getCurrentDir().sanitizePath 350 | for param in commandLineParams(): 351 | let flag = if param.len() <= 2: param else: param[0..<2] 352 | 353 | if fileExists(param): 354 | if param.splitFile().ext.toLowerAscii() == ".cfg": 355 | runCfg(param) 356 | else: 357 | files.add(param) 358 | 359 | elif flag == "-C": 360 | cfg["compile." & $uniq] = param[2..^1] 361 | elif flag == "-E": 362 | gExcludes.add(param[2..^1].addEnv().sanitizePath) 363 | elif flag == "-F": 364 | if cfg.hasKey("flags"): 365 | cfg["flags"] = cfg["flags"] & " " & param[2..^1] 366 | else: 367 | cfg["flags"] = param[2..^1] 368 | elif flag == "-I": 369 | gIncludes.add(param[2..^1].addEnv().sanitizePath) 370 | elif flag == "-O": 371 | setOutputDir(param[2..^1]) 372 | elif flag == "-P": 373 | if cfg.hasKey("ppflags"): 374 | cfg["ppflags"] = cfg["ppflags"] & " " & param[2..^1] 375 | else: 376 | cfg["ppflags"] = param[2..^1] 377 | 378 | elif flag == "-c": 379 | cfg["ctags"] = "true" 380 | elif flag == "-d": 381 | cfg["defines"] = "true" 382 | elif flag == "-i": 383 | cfg["inline"] = "true" 384 | elif flag == "-n": 385 | cfg["noprocess"] = "true" 386 | elif flag == "-p": 387 | cfg["preprocess"] = "true" 388 | elif flag == "-r": 389 | cfg["recurse"] = "true" 390 | 391 | elif flag == "-a": 392 | cfg["append." & $uniq] = param[2..^1] 393 | elif flag == "-e": 394 | cfg["prepend." & $uniq] = param[2..^1] 395 | elif flag == "-l": 396 | cfg["replace." & $uniq] = param[2..^1] 397 | elif flag == "-o": 398 | cfg["comment." & $uniq] = param[2..^1] 399 | elif flag == "-s": 400 | cfg["search." & $uniq] = param[2..^1] 401 | elif flag == "-x": 402 | cfg["regex." & $uniq] = param[2..^1] 403 | 404 | elif param == "-h" or param == "-?" or param == "--help": 405 | echo gHelp 406 | quit(0) 407 | 408 | uniq += 1 409 | 410 | for file in files: 411 | runFile(file, cfg) -------------------------------------------------------------------------------- /tests/nimgentest.nims: -------------------------------------------------------------------------------- 1 | import distros, ospaths, strutils 2 | 3 | var 4 | pygonly = false 5 | comps = @[ 6 | "nimbigwig", 7 | "nimclipboard", "nimfuzzy", 8 | "nimmonocypher", 9 | #"nimnuklear", 10 | "nimrax", 11 | "nimssh2", 12 | "nimtess2" 13 | ] 14 | 15 | let 16 | gccver = staticExec("gcc --version").split("\n")[0].split(" ")[^1] 17 | nimver = staticExec("nim -v").split("\n")[0].split(" ")[3] 18 | 19 | #if nimver >= "0.19.0" and (gccver >= "5.0.0" or detectOs(MacOSX)): 20 | # comps.add("nimfastText") 21 | 22 | if nimver >= "1.2.0": 23 | # csize_t 24 | comps.add("nim7z") 25 | 26 | if detectOs(Windows): 27 | comps.add("nimkerberos") 28 | 29 | if not detectOs(MacOSX): 30 | comps.add("nimzbar") 31 | 32 | echo "Nim version: " & nimver 33 | echo "GCC version: " & gccver 34 | 35 | echo "Testing comps:" 36 | for comp in comps: 37 | echo " " & comp 38 | 39 | if paramCount() > 2: 40 | for i in 3 .. paramCount(): 41 | if paramStr(i) == "--pygonly": 42 | pygonly = true 43 | elif paramStr(i).len() > 10 and "--comps=" in paramStr(i)[0 ..< 8]: 44 | comps = paramStr(i)[8 .. ^1].split(",") 45 | 46 | for comp in comps: 47 | if not pygonly: 48 | if not dirExists(".."/comp): 49 | withDir(".."): 50 | exec "git clone --depth=1 https://github.com/genotrance/" & comp 51 | 52 | echo gorgeEx("nimble uninstall -y " & comp).output 53 | withDir(".."/comp): 54 | exec "git pull" 55 | 56 | rmDir(comp) 57 | 58 | exec "nimble install -y" 59 | exec "nimble test" 60 | 61 | exec "nimble install -y" 62 | exec "nimble test" 63 | 64 | when defined(windows): 65 | if not pygonly: 66 | if dirExists("web"/comp): 67 | rmDir("web"/comp) 68 | 69 | mkDir("web"/comp) 70 | for file in listFiles(".."/comp/comp) & listFiles(".."/comp): 71 | if file.splitFile().ext == ".nim": 72 | cpFile(file, "web"/comp/extractFilename(file)) 73 | 74 | cpFile("web"/"nimdoc.cfg", "web"/comp/"nimdoc.cfg") 75 | withDir("web"/comp): 76 | for file in listFiles("."): 77 | if file.splitFile().ext == ".nim": 78 | if not pygonly: 79 | exec "nim doc --git.url:. --index:on -o:" & file.changeFileExt("html") & " " & file 80 | exec "pygmentize -f html -O full,linenos=1,anchorlinenos=True,lineanchors=L,style=vs -o " & file & ".html " & file 81 | 82 | if not pygonly: 83 | exec "nim buildIndex -o:index.html ." 84 | rmFile("web"/comp/"nimdoc.cfg") 85 | -------------------------------------------------------------------------------- /tests/rununittests.nim: -------------------------------------------------------------------------------- 1 | import os, osproc, strutils 2 | 3 | proc main() = 4 | var failures = 0 5 | for file in walkFiles(currentSourcePath().splitPath().head / "unittests/*.nim"): 6 | let (path, fname, ext) = file.splitFile() 7 | if fname.startswith("test"): 8 | failures += execCmd "nim c -r " & file 9 | quit(failures) 10 | main() 11 | -------------------------------------------------------------------------------- /tests/unittests/common.nim: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | proc checkFile*(filepath, expected: string) = 4 | let result = readFile(filepath) 5 | check result == expected 6 | -------------------------------------------------------------------------------- /tests/unittests/data/teststaticexpectedfrontbraces.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file ipc.h 3 | * @brief Inter-process communication handling 4 | * @author plutoo 5 | * @copyright libnx Authors 6 | */ 7 | #pragma once 8 | #include "../result.h" 9 | #include "../arm/tls.h" 10 | #include "../kernel/svc.h" 11 | 12 | /// IPC input header magic 13 | #define SFCI_MAGIC 0x49434653 14 | /// IPC output header magic 15 | #define SFCO_MAGIC 0x4f434653 16 | 17 | /// IPC invalid object ID 18 | #define IPC_INVALID_OBJECT_ID UINT32_MAX 19 | 20 | ///@name IPC request building 21 | ///@{ 22 | 23 | /// IPC command (request) structure. 24 | #define IPC_MAX_BUFFERS 8 25 | #define IPC_MAX_OBJECTS 8 26 | 27 | typedef enum { 28 | BufferType_Normal=0, ///< Regular buffer. 29 | BufferType_Type1=1, ///< Allows ProcessMemory and shared TransferMemory. 30 | BufferType_Invalid=2, 31 | BufferType_Type3=3 ///< Same as Type1 except remote process is not allowed to use device-mapping. 32 | } BufferType; 33 | 34 | typedef enum { 35 | BufferDirection_Send=0, 36 | BufferDirection_Recv=1, 37 | BufferDirection_Exch=2, 38 | } BufferDirection; 39 | 40 | typedef enum { 41 | IpcCommandType_Invalid = 0, 42 | IpcCommandType_LegacyRequest = 1, 43 | IpcCommandType_Close = 2, 44 | IpcCommandType_LegacyControl = 3, 45 | IpcCommandType_Request = 4, 46 | IpcCommandType_Control = 5, 47 | IpcCommandType_RequestWithContext = 6, 48 | IpcCommandType_ControlWithContext = 7, 49 | } IpcCommandType; 50 | 51 | typedef enum { 52 | DomainMessageType_Invalid = 0, 53 | DomainMessageType_SendMessage = 1, 54 | DomainMessageType_Close = 2, 55 | } DomainMessageType; 56 | 57 | /// IPC domain message header. 58 | typedef struct { 59 | u8 Type; 60 | u8 NumObjectIds; 61 | u16 Length; 62 | u32 ThisObjectId; 63 | u32 Pad[2]; 64 | } DomainMessageHeader; 65 | 66 | typedef struct { 67 | size_t NumSend; // A 68 | size_t NumRecv; // B 69 | size_t NumExch; // W 70 | const void* Buffers[IPC_MAX_BUFFERS]; 71 | size_t BufferSizes[IPC_MAX_BUFFERS]; 72 | BufferType BufferTypes[IPC_MAX_BUFFERS]; 73 | 74 | size_t NumStaticIn; // X 75 | size_t NumStaticOut; // C 76 | const void* Statics[IPC_MAX_BUFFERS]; 77 | size_t StaticSizes[IPC_MAX_BUFFERS]; 78 | u8 StaticIndices[IPC_MAX_BUFFERS]; 79 | 80 | bool SendPid; 81 | size_t NumHandlesCopy; 82 | size_t NumHandlesMove; 83 | Handle Handles[IPC_MAX_OBJECTS]; 84 | 85 | size_t NumObjectIds; 86 | u32 ObjectIds[IPC_MAX_OBJECTS]; 87 | } IpcCommand; 88 | 89 | /** 90 | * @brief Initializes an IPC command structure. 91 | * @param cmd IPC command structure. 92 | */ 93 | static inline void ipcInitialize(IpcCommand* cmd);//{ 94 | // *cmd = (IpcCommand){0}; 95 | //} 96 | 97 | /// IPC buffer descriptor. 98 | typedef struct { 99 | u32 Size; ///< Size of the buffer. 100 | u32 Addr; ///< Lower 32-bits of the address of the buffer 101 | u32 Packed; ///< Packed data (including higher bits of the address) 102 | } IpcBufferDescriptor; 103 | 104 | /// IPC static send-buffer descriptor. 105 | typedef struct { 106 | u32 Packed; ///< Packed data (including higher bits of the address) 107 | u32 Addr; ///< Lower 32-bits of the address 108 | } IpcStaticSendDescriptor; 109 | 110 | /// IPC static receive-buffer descriptor. 111 | typedef struct { 112 | u32 Addr; ///< Lower 32-bits of the address of the buffer 113 | u32 Packed; ///< Packed data (including higher bits of the address) 114 | } IpcStaticRecvDescriptor; 115 | 116 | /** 117 | * @brief Adds a buffer to an IPC command structure. 118 | * @param cmd IPC command structure. 119 | * @param buffer Address of the buffer. 120 | * @param size Size of the buffer. 121 | * @param type Buffer type. 122 | */ 123 | static inline void ipcAddSendBuffer(IpcCommand* cmd, const void* buffer, size_t size, BufferType type);//{ 124 | // size_t off = cmd->NumSend; 125 | // cmd->Buffers[off] = buffer; 126 | // cmd->BufferSizes[off] = size; 127 | // cmd->BufferTypes[off] = type; 128 | // cmd->NumSend++; 129 | //} 130 | 131 | /** 132 | * @brief Adds a receive-buffer to an IPC command structure. 133 | * @param cmd IPC command structure. 134 | * @param buffer Address of the buffer. 135 | * @param size Size of the buffer. 136 | * @param type Buffer type. 137 | */ 138 | static inline void ipcAddRecvBuffer(IpcCommand* cmd, void* buffer, size_t size, BufferType type);//{ 139 | // size_t off = cmd->NumSend + cmd->NumRecv; 140 | // cmd->Buffers[off] = buffer; 141 | // cmd->BufferSizes[off] = size; 142 | // cmd->BufferTypes[off] = type; 143 | // cmd->NumRecv++; 144 | //} 145 | 146 | /** 147 | * @brief Adds an exchange-buffer to an IPC command structure. 148 | * @param cmd IPC command structure. 149 | * @param buffer Address of the buffer. 150 | * @param size Size of the buffer. 151 | * @param type Buffer type. 152 | */ 153 | static inline void ipcAddExchBuffer(IpcCommand* cmd, void* buffer, size_t size, BufferType type);//{ 154 | // size_t off = cmd->NumSend + cmd->NumRecv + cmd->NumExch; 155 | // cmd->Buffers[off] = buffer; 156 | // cmd->BufferSizes[off] = size; 157 | // cmd->BufferTypes[off] = type; 158 | // cmd->NumExch++; 159 | //} 160 | 161 | /** 162 | * @brief Adds a static-buffer to an IPC command structure. 163 | * @param cmd IPC command structure. 164 | * @param buffer Address of the buffer. 165 | * @param size Size of the buffer. 166 | * @param index Index of buffer. 167 | */ 168 | static inline void ipcAddSendStatic(IpcCommand* cmd, const void* buffer, size_t size, u8 index);//{ 169 | // size_t off = cmd->NumStaticIn; 170 | // cmd->Statics[off] = buffer; 171 | // cmd->StaticSizes[off] = size; 172 | // cmd->StaticIndices[off] = index; 173 | // cmd->NumStaticIn++; 174 | //} 175 | 176 | /** 177 | * @brief Adds a static-receive-buffer to an IPC command structure. 178 | * @param cmd IPC command structure. 179 | * @param buffer Address of the buffer. 180 | * @param size Size of the buffer. 181 | * @param index Index of buffer. 182 | */ 183 | static inline void ipcAddRecvStatic(IpcCommand* cmd, void* buffer, size_t size, u8 index);//{ 184 | // size_t off = cmd->NumStaticIn + cmd->NumStaticOut; 185 | // cmd->Statics[off] = buffer; 186 | // cmd->StaticSizes[off] = size; 187 | // cmd->StaticIndices[off] = index; 188 | // cmd->NumStaticOut++; 189 | //} 190 | 191 | /** 192 | * @brief Adds a smart-buffer (buffer + static-buffer pair) to an IPC command structure. 193 | * @param cmd IPC command structure. 194 | * @param ipc_buffer_size IPC buffer size. 195 | * @param buffer Address of the buffer. 196 | * @param size Size of the buffer. 197 | * @param index Index of buffer. 198 | */ 199 | static inline void ipcAddSendSmart(IpcCommand* cmd, size_t ipc_buffer_size, const void* buffer, size_t size, u8 index);//{ 200 | // if (ipc_buffer_size != 0 && size <= ipc_buffer_size) { 201 | // ipcAddSendBuffer(cmd, NULL, 0, BufferType_Normal); 202 | // ipcAddSendStatic(cmd, buffer, size, index); 203 | // } else { 204 | // ipcAddSendBuffer(cmd, buffer, size, BufferType_Normal); 205 | // ipcAddSendStatic(cmd, NULL, 0, index); 206 | // } 207 | //} 208 | 209 | /** 210 | * @brief Adds a smart-receive-buffer (buffer + static-receive-buffer pair) to an IPC command structure. 211 | * @param cmd IPC command structure. 212 | * @param ipc_buffer_size IPC buffer size. 213 | * @param buffer Address of the buffer. 214 | * @param size Size of the buffer. 215 | * @param index Index of buffer. 216 | */ 217 | static inline void ipcAddRecvSmart(IpcCommand* cmd, size_t ipc_buffer_size, void* buffer, size_t size, u8 index);//{ 218 | // if (ipc_buffer_size != 0 && size <= ipc_buffer_size) { 219 | // ipcAddRecvBuffer(cmd, NULL, 0, BufferType_Normal); 220 | // ipcAddRecvStatic(cmd, buffer, size, index); 221 | // } else { 222 | // ipcAddRecvBuffer(cmd, buffer, size, BufferType_Normal); 223 | // ipcAddRecvStatic(cmd, NULL, 0, index); 224 | // } 225 | //} 226 | 227 | /** 228 | * @brief Tags an IPC command structure to send the PID. 229 | * @param cmd IPC command structure. 230 | */ 231 | static inline void ipcSendPid(IpcCommand* cmd);//{ 232 | // cmd->SendPid = true; 233 | //} 234 | 235 | /** 236 | * @brief Adds a copy-handle to be sent through an IPC command structure. 237 | * @param cmd IPC command structure. 238 | * @param h Handle to send. 239 | * @remark The receiving process gets a copy of the handle. 240 | */ 241 | static inline void ipcSendHandleCopy(IpcCommand* cmd, Handle h);//{ 242 | // cmd->Handles[cmd->NumHandlesCopy++] = h; 243 | //} 244 | 245 | /** 246 | * @brief Adds a move-handle to be sent through an IPC command structure. 247 | * @param cmd IPC command structure. 248 | * @param h Handle to send. 249 | * @remark The sending process loses ownership of the handle, which is transferred to the receiving process. 250 | */ 251 | static inline void ipcSendHandleMove(IpcCommand* cmd, Handle h);//{ 252 | // cmd->Handles[cmd->NumHandlesCopy + cmd->NumHandlesMove++] = h; 253 | //} 254 | 255 | /** 256 | * @brief Prepares the header of an IPC command structure. 257 | * @param cmd IPC command structure. 258 | * @param sizeof_raw Size in bytes of the raw data structure to embed inside the IPC request 259 | * @return Pointer to the raw embedded data structure in the request, ready to be filled out. 260 | */ 261 | static inline void* ipcPrepareHeader(IpcCommand* cmd, size_t sizeof_raw);//{ 262 | // u32* buf = (u32*)armGetTls(); 263 | // size_t i; 264 | // *buf++ = IpcCommandType_Request | (cmd->NumStaticIn << 16) | (cmd->NumSend << 20) | (cmd->NumRecv << 24) | (cmd->NumExch << 28); 265 | // 266 | // u32* fill_in_size_later = buf; 267 | // 268 | // if (cmd->NumStaticOut > 0) { 269 | // *buf = (cmd->NumStaticOut + 2) << 10; 270 | // } 271 | // else { 272 | // *buf = 0; 273 | // } 274 | // 275 | // if (cmd->SendPid || cmd->NumHandlesCopy > 0 || cmd->NumHandlesMove > 0) { 276 | // *buf++ |= 0x80000000; 277 | // *buf++ = (!!cmd->SendPid) | (cmd->NumHandlesCopy << 1) | (cmd->NumHandlesMove << 5); 278 | // 279 | // if (cmd->SendPid) 280 | // buf += 2; 281 | // 282 | // for (i=0; i<(cmd->NumHandlesCopy + cmd->NumHandlesMove); i++) 283 | // *buf++ = cmd->Handles[i]; 284 | // } 285 | // else { 286 | // buf++; 287 | // } 288 | // 289 | // for (i=0; iNumStaticIn; i++, buf+=2) { 290 | // IpcStaticSendDescriptor* desc = (IpcStaticSendDescriptor*) buf; 291 | // 292 | // uintptr_t ptr = (uintptr_t) cmd->Statics[i]; 293 | // desc->Addr = ptr; 294 | // desc->Packed = cmd->StaticIndices[i] | (cmd->StaticSizes[i] << 16) | 295 | // (((ptr >> 32) & 15) << 12) | (((ptr >> 36) & 15) << 6); 296 | // } 297 | // 298 | // for (i=0; i<(cmd->NumSend + cmd->NumRecv + cmd->NumExch); i++, buf+=3) { 299 | // IpcBufferDescriptor* desc = (IpcBufferDescriptor*) buf; 300 | // desc->Size = cmd->BufferSizes[i]; 301 | // 302 | // uintptr_t ptr = (uintptr_t) cmd->Buffers[i]; 303 | // desc->Addr = ptr; 304 | // desc->Packed = cmd->BufferTypes[i] | 305 | // (((ptr >> 32) & 15) << 28) | ((ptr >> 36) << 2); 306 | // } 307 | // 308 | // u32 padding = ((16 - (((uintptr_t) buf) & 15)) & 15) / 4; 309 | // u32* raw = (u32*) (buf + padding); 310 | // 311 | // size_t raw_size = (sizeof_raw/4) + 4; 312 | // buf += raw_size; 313 | // 314 | // u16* buf_u16 = (u16*) buf; 315 | // 316 | // for (i=0; iNumStaticOut; i++) { 317 | // size_t off = cmd->NumStaticIn + i; 318 | // size_t sz = (uintptr_t) cmd->StaticSizes[off]; 319 | // 320 | // buf_u16[i] = (sz > 0xFFFF) ? 0 : sz; 321 | // } 322 | // 323 | // size_t u16s_size = ((2*cmd->NumStaticOut) + 3)/4; 324 | // buf += u16s_size; 325 | // raw_size += u16s_size; 326 | // 327 | // *fill_in_size_later |= raw_size; 328 | // 329 | // for (i=0; iNumStaticOut; i++, buf+=2) { 330 | // IpcStaticRecvDescriptor* desc = (IpcStaticRecvDescriptor*) buf; 331 | // size_t off = cmd->NumStaticIn + i; 332 | // 333 | // uintptr_t ptr = (uintptr_t) cmd->Statics[off]; 334 | // desc->Addr = ptr; 335 | // desc->Packed = (ptr >> 32) | (cmd->StaticSizes[off] << 16); 336 | // } 337 | // 338 | // return (void*) raw; 339 | //} 340 | 341 | /** 342 | * @brief Dispatches an IPC request. 343 | * @param session IPC session handle. 344 | * @return Result code. 345 | */ 346 | static inline Result ipcDispatch(Handle session);//{ 347 | // return svcSendSyncRequest(session); 348 | //} 349 | 350 | ///@} 351 | 352 | ///@name IPC response parsing 353 | ///@{ 354 | 355 | /// IPC parsed command (response) structure. 356 | typedef struct { 357 | IpcCommandType CommandType; ///< Type of the command 358 | 359 | bool HasPid; ///< true if the 'Pid' field is filled out. 360 | u64 Pid; ///< PID included in the response (only if HasPid is true) 361 | 362 | size_t NumHandles; ///< Number of handles copied. 363 | Handle Handles[IPC_MAX_OBJECTS]; ///< Handles. 364 | bool WasHandleCopied[IPC_MAX_OBJECTS]; ///< true if the handle was moved, false if it was copied. 365 | 366 | bool IsDomainMessage; ///< true if the the message is a Domain message. 367 | DomainMessageType MessageType; ///< Type of the domain message. 368 | u32 MessageLength; ///< Size of rawdata (for domain messages). 369 | u32 ThisObjectId; ///< Object ID to call the command on (for domain messages). 370 | size_t NumObjectIds; ///< Number of object IDs (for domain messages). 371 | u32 ObjectIds[IPC_MAX_OBJECTS]; ///< Object IDs (for domain messages). 372 | 373 | size_t NumBuffers; ///< Number of buffers in the response. 374 | void* Buffers[IPC_MAX_BUFFERS]; ///< Pointers to the buffers. 375 | size_t BufferSizes[IPC_MAX_BUFFERS]; ///< Sizes of the buffers. 376 | BufferType BufferTypes[IPC_MAX_BUFFERS]; ///< Types of the buffers. 377 | BufferDirection BufferDirections[IPC_MAX_BUFFERS]; ///< Direction of each buffer. 378 | 379 | size_t NumStatics; ///< Number of statics in the response. 380 | void* Statics[IPC_MAX_BUFFERS]; ///< Pointers to the statics. 381 | size_t StaticSizes[IPC_MAX_BUFFERS]; ///< Sizes of the statics. 382 | u8 StaticIndices[IPC_MAX_BUFFERS]; ///< Indices of the statics. 383 | 384 | size_t NumStaticsOut; ///< Number of output statics available in the response. 385 | 386 | void* Raw; ///< Pointer to the raw embedded data structure in the response. 387 | void* RawWithoutPadding; ///< Pointer to the raw embedded data structure, without padding. 388 | size_t RawSize; ///< Size of the raw embedded data. 389 | } IpcParsedCommand; 390 | 391 | /** 392 | * @brief Parse an IPC command response into an IPC parsed command structure. 393 | * @param IPC parsed command structure to fill in. 394 | * @return Result code. 395 | */ 396 | static inline Result ipcParse(IpcParsedCommand* r);//{ 397 | // u32* buf = (u32*)armGetTls(); 398 | // u32 ctrl0 = *buf++; 399 | // u32 ctrl1 = *buf++; 400 | // size_t i; 401 | // 402 | // r->IsDomainMessage = false; 403 | // 404 | // r->CommandType = (IpcCommandType) (ctrl0 & 0xffff); 405 | // r->HasPid = false; 406 | // r->RawSize = (ctrl1 & 0x1ff) * 4; 407 | // r->NumHandles = 0; 408 | // 409 | // r->NumStaticsOut = (ctrl1 >> 10) & 15; 410 | // if (r->NumStaticsOut >> 1) r->NumStaticsOut--; // Value 2 -> Single descriptor 411 | // if (r->NumStaticsOut >> 1) r->NumStaticsOut--; // Value 3+ -> (Value - 2) descriptors 412 | // 413 | // if (ctrl1 & 0x80000000) { 414 | // u32 ctrl2 = *buf++; 415 | // 416 | // if (ctrl2 & 1) { 417 | // r->HasPid = true; 418 | // r->Pid = *buf++; 419 | // r->Pid |= ((u64)(*buf++)) << 32; 420 | // } 421 | // 422 | // size_t num_handles_copy = ((ctrl2 >> 1) & 15); 423 | // size_t num_handles_move = ((ctrl2 >> 5) & 15); 424 | // 425 | // size_t num_handles = num_handles_copy + num_handles_move; 426 | // u32* buf_after_handles = buf + num_handles; 427 | // 428 | // if (num_handles > IPC_MAX_OBJECTS) 429 | // num_handles = IPC_MAX_OBJECTS; 430 | // 431 | // for (i=0; iHandles[i] = *(buf+i); 434 | // r->WasHandleCopied[i] = (i < num_handles_copy); 435 | // } 436 | // 437 | // r->NumHandles = num_handles; 438 | // buf = buf_after_handles; 439 | // } 440 | // 441 | // size_t num_statics = (ctrl0 >> 16) & 15; 442 | // u32* buf_after_statics = buf + num_statics*2; 443 | // 444 | // if (num_statics > IPC_MAX_BUFFERS) 445 | // num_statics = IPC_MAX_BUFFERS; 446 | // 447 | // for (i=0; iPacked; 450 | // 451 | // r->Statics[i] = (void*) (desc->Addr | (((packed >> 12) & 15) << 32) | (((packed >> 6) & 15) << 36)); 452 | // r->StaticSizes[i] = packed >> 16; 453 | // r->StaticIndices[i] = packed & 63; 454 | // } 455 | // 456 | // r->NumStatics = num_statics; 457 | // buf = buf_after_statics; 458 | // 459 | // size_t num_bufs_send = (ctrl0 >> 20) & 15; 460 | // size_t num_bufs_recv = (ctrl0 >> 24) & 15; 461 | // size_t num_bufs_exch = (ctrl0 >> 28) & 15; 462 | // 463 | // size_t num_bufs = num_bufs_send + num_bufs_recv + num_bufs_exch; 464 | // r->Raw = (void*)(((uintptr_t)(buf + num_bufs*3) + 15) &~ 15); 465 | // r->RawWithoutPadding = (void*)((uintptr_t)(buf + num_bufs*3)); 466 | // 467 | // if (num_bufs > IPC_MAX_BUFFERS) 468 | // num_bufs = IPC_MAX_BUFFERS; 469 | // 470 | // for (i=0; iPacked; 473 | // 474 | // r->Buffers[i] = (void*) (desc->Addr | ((packed >> 28) << 32) | (((packed >> 2) & 15) << 36)); 475 | // r->BufferSizes[i] = desc->Size; 476 | // r->BufferTypes[i] = (BufferType) (packed & 3); 477 | // 478 | // if (i < num_bufs_send) 479 | // r->BufferDirections[i] = BufferDirection_Send; 480 | // else if (i < (num_bufs_send + num_bufs_recv)) 481 | // r->BufferDirections[i] = BufferDirection_Recv; 482 | // else 483 | // r->BufferDirections[i] = BufferDirection_Exch; 484 | // } 485 | // 486 | // r->NumBuffers = num_bufs; 487 | // return 0; 488 | //} 489 | 490 | /** 491 | * @brief Queries the size of an IPC pointer buffer. 492 | * @param session IPC session handle. 493 | * @param size Output variable in which to store the size. 494 | * @return Result code. 495 | */ 496 | static inline Result ipcQueryPointerBufferSize(Handle session, size_t *size);//{ 497 | // u32* buf = (u32*)armGetTls(); 498 | // 499 | // buf[0] = IpcCommandType_Control; 500 | // buf[1] = 8; 501 | // buf[2] = 0; 502 | // buf[3] = 0; 503 | // buf[4] = SFCI_MAGIC; 504 | // buf[5] = 0; 505 | // buf[6] = 3; 506 | // buf[7] = 0; 507 | // 508 | // Result rc = ipcDispatch(session); 509 | // 510 | // if (R_SUCCEEDED(rc)) { 511 | // IpcParsedCommand r; 512 | // ipcParse(&r); 513 | // 514 | // struct ipcQueryPointerBufferSizeResponse { 515 | // u64 magic; 516 | // u64 result; 517 | // u32 size; 518 | // } *raw = (struct ipcQueryPointerBufferSizeResponse*)r.Raw; 519 | // 520 | // rc = raw->result; 521 | // 522 | // if (R_SUCCEEDED(rc)) { 523 | // *size = raw->size & 0xffff; 524 | // } 525 | // } 526 | // 527 | // return rc; 528 | //} 529 | 530 | /** 531 | * @brief Closes the IPC session with proper clean up. 532 | * @param session IPC session handle. 533 | * @return Result code. 534 | */ 535 | static inline Result ipcCloseSession(Handle session);//{ 536 | // u32* buf = (u32*)armGetTls(); 537 | // buf[0] = IpcCommandType_Close; 538 | // buf[1] = 0; 539 | // return ipcDispatch(session); 540 | //} 541 | ///@} 542 | 543 | ///@name IPC domain handling 544 | ///@{ 545 | 546 | /** 547 | * @brief Converts an IPC session handle into a domain. 548 | * @param session IPC session handle. 549 | * @param object_id_out Output variable in which to store the object ID. 550 | * @return Result code. 551 | */ 552 | static inline Result ipcConvertSessionToDomain(Handle session, u32* object_id_out);//{ 553 | // u32* buf = (u32*)armGetTls(); 554 | // 555 | // buf[0] = IpcCommandType_Control; 556 | // buf[1] = 8; 557 | // buf[4] = SFCI_MAGIC; 558 | // buf[5] = 0; 559 | // buf[6] = 0; 560 | // buf[7] = 0; 561 | // 562 | // Result rc = ipcDispatch(session); 563 | // 564 | // if (R_SUCCEEDED(rc)) { 565 | // IpcParsedCommand r; 566 | // ipcParse(&r); 567 | // 568 | // struct ipcConvertSessionToDomainResponse { 569 | // u64 magic; 570 | // u64 result; 571 | // u32 object_id; 572 | // } *raw = (struct ipcConvertSessionToDomainResponse*)r.Raw; 573 | // 574 | // rc = raw->result; 575 | // 576 | // if (R_SUCCEEDED(rc)) { 577 | // *object_id_out = raw->object_id; 578 | // } 579 | // } 580 | // 581 | // return rc; 582 | //} 583 | 584 | /** 585 | * @brief Adds an object ID to be sent through an IPC domain command structure. 586 | * @param cmd IPC domain command structure. 587 | * @param object_id Object ID to send. 588 | */ 589 | static inline void ipcSendObjectId(IpcCommand* cmd, u32 object_id);//{ 590 | // cmd->ObjectIds[cmd->NumObjectIds++] = object_id; 591 | //} 592 | 593 | /** 594 | * @brief Prepares the header of an IPC command structure (domain version). 595 | * @param cmd IPC command structure. 596 | * @param sizeof_raw Size in bytes of the raw data structure to embed inside the IPC request 597 | * @oaram object_id Domain object ID. 598 | * @return Pointer to the raw embedded data structure in the request, ready to be filled out. 599 | */ 600 | static inline void* ipcPrepareHeaderForDomain(IpcCommand* cmd, size_t sizeof_raw, u32 object_id);//{ 601 | // void* raw = ipcPrepareHeader(cmd, sizeof_raw + sizeof(DomainMessageHeader)); 602 | // DomainMessageHeader* hdr = (DomainMessageHeader*) raw; 603 | // u32 *object_ids = (u32*)(((uintptr_t) raw) + sizeof(DomainMessageHeader) + sizeof_raw); 604 | // 605 | // hdr->Type = DomainMessageType_SendMessage; 606 | // hdr->NumObjectIds = (u8)cmd->NumObjectIds; 607 | // hdr->Length = sizeof_raw; 608 | // hdr->ThisObjectId = object_id; 609 | // hdr->Pad[0] = hdr->Pad[1] = 0; 610 | // 611 | // for(size_t i = 0; i < cmd->NumObjectIds; i++) 612 | // object_ids[i] = cmd->ObjectIds[i]; 613 | // return (void*)(((uintptr_t) raw) + sizeof(DomainMessageHeader)); 614 | //} 615 | 616 | /** 617 | * @brief Parse an IPC command response into an IPC parsed command structure (domain version). 618 | * @param IPC parsed command structure to fill in. 619 | * @return Result code. 620 | */ 621 | static inline Result ipcParseForDomain(IpcParsedCommand* r);//{ 622 | // Result rc = ipcParse(r); 623 | // DomainMessageHeader *hdr; 624 | // u32 *object_ids; 625 | // if(R_FAILED(rc)) 626 | // return rc; 627 | // 628 | // hdr = (DomainMessageHeader*) r->Raw; 629 | // object_ids = (u32*)(((uintptr_t) hdr) + sizeof(DomainMessageHeader) + hdr->Length); 630 | // r->Raw = (void*)(((uintptr_t) r->Raw) + sizeof(DomainMessageHeader)); 631 | // 632 | // r->IsDomainMessage = true; 633 | // r->MessageType = (DomainMessageType)(hdr->Type); 634 | // switch (r->MessageType) { 635 | // case DomainMessageType_SendMessage: 636 | // case DomainMessageType_Close: 637 | // break; 638 | // default: 639 | // return MAKERESULT(Module_Libnx, LibnxError_DomainMessageUnknownType); 640 | // } 641 | // r->ThisObjectId = hdr->ThisObjectId; 642 | // r->NumObjectIds = hdr->NumObjectIds > 8 ? 8 : hdr->NumObjectIds; 643 | // if ((uintptr_t)object_ids + sizeof(u32) * r->NumObjectIds - (uintptr_t)armGetTls() >= 0x100) { 644 | // return MAKERESULT(Module_Libnx, LibnxError_DomainMessageTooManyObjectIds); 645 | // } 646 | // for(size_t i = 0; i < r->NumObjectIds; i++) 647 | // r->ObjectIds[i] = object_ids[i]; 648 | // 649 | // return rc; 650 | //} 651 | 652 | /** 653 | * @brief Closes a domain object by ID. 654 | * @param session IPC session handle. 655 | * @param object_id ID of the object to close. 656 | * @return Result code. 657 | */ 658 | static inline Result ipcCloseObjectById(Handle session, u32 object_id);//{ 659 | // IpcCommand c; 660 | // DomainMessageHeader* hdr; 661 | // 662 | // ipcInitialize(&c); 663 | // hdr = (DomainMessageHeader*)ipcPrepareHeader(&c, sizeof(DomainMessageHeader)); 664 | // 665 | // hdr->Type = 2; 666 | // hdr->NumObjectIds = 0; 667 | // hdr->Length = 0; 668 | // hdr->ThisObjectId = object_id; 669 | // hdr->Pad[0] = hdr->Pad[1] = 0; 670 | // 671 | // return ipcDispatch(session); // this command has no associated response 672 | //} 673 | 674 | ///@} 675 | 676 | -------------------------------------------------------------------------------- /tests/unittests/data/teststaticexpectednewlinebraces.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file condvar.h 3 | * @brief Condition variable synchronization primitive. 4 | * @author plutoo 5 | * @copyright libnx Authors 6 | */ 7 | #pragma once 8 | #include "../types.h" 9 | #include "../kernel/mutex.h" 10 | 11 | /// Condition variable structure. 12 | typedef struct { 13 | u32 tag; 14 | Mutex* mutex; 15 | } CondVar; 16 | 17 | /** 18 | * @brief Initializes a condition variable. 19 | * @param[in] c Condition variable object. 20 | * @param[in] m Mutex object to use inside the condition variable. 21 | */ 22 | void condvarInit(CondVar* c, Mutex* m); 23 | 24 | /** 25 | * @brief Waits on a condition variable with a timeout. 26 | * @param[in] c Condition variable object. 27 | * @param[in] timeout Timeout in nanoseconds. 28 | * @return Result code (0xEA01 on timeout). 29 | * @remark On function return, the underlying mutex is acquired. 30 | */ 31 | Result condvarWaitTimeout(CondVar* c, u64 timeout); 32 | 33 | /** 34 | * @brief Waits on a condition variable. 35 | * @param[in] c Condition variable object. 36 | * @return Result code. 37 | * @remark On function return, the underlying mutex is acquired. 38 | */ 39 | static inline Result condvarWait(CondVar* c);//{ 40 | // return condvarWaitTimeout(c, -1ull); 41 | //} 42 | 43 | /** 44 | * @brief Wakes up up to the specified number of threads waiting on a condition variable. 45 | * @param[in] c Condition variable object. 46 | * @param[in] num Maximum number of threads to wake up (or -1 to wake them all up). 47 | * @return Result code. 48 | */ 49 | Result condvarWake(CondVar* c, int num); 50 | 51 | /** 52 | * @brief Wakes up a single thread waiting on a condition variable. 53 | * @param[in] c Condition variable object. 54 | * @return Result code. 55 | */ 56 | static inline Result condvarWakeOne(CondVar* c);//{ 57 | // return condvarWake(c, 1); 58 | //} 59 | 60 | /** 61 | * @brief Wakes up all thread waiting on a condition variable. 62 | * @param[in] c Condition variable object. 63 | * @return Result code. 64 | */ 65 | static inline Result condvarWakeAll(CondVar* c);//{ 66 | // return condvarWake(c, -1); 67 | //} 68 | 69 | -------------------------------------------------------------------------------- /tests/unittests/data/teststaticfrontbraces.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file ipc.h 3 | * @brief Inter-process communication handling 4 | * @author plutoo 5 | * @copyright libnx Authors 6 | */ 7 | #pragma once 8 | #include "../result.h" 9 | #include "../arm/tls.h" 10 | #include "../kernel/svc.h" 11 | 12 | /// IPC input header magic 13 | #define SFCI_MAGIC 0x49434653 14 | /// IPC output header magic 15 | #define SFCO_MAGIC 0x4f434653 16 | 17 | /// IPC invalid object ID 18 | #define IPC_INVALID_OBJECT_ID UINT32_MAX 19 | 20 | ///@name IPC request building 21 | ///@{ 22 | 23 | /// IPC command (request) structure. 24 | #define IPC_MAX_BUFFERS 8 25 | #define IPC_MAX_OBJECTS 8 26 | 27 | typedef enum { 28 | BufferType_Normal=0, ///< Regular buffer. 29 | BufferType_Type1=1, ///< Allows ProcessMemory and shared TransferMemory. 30 | BufferType_Invalid=2, 31 | BufferType_Type3=3 ///< Same as Type1 except remote process is not allowed to use device-mapping. 32 | } BufferType; 33 | 34 | typedef enum { 35 | BufferDirection_Send=0, 36 | BufferDirection_Recv=1, 37 | BufferDirection_Exch=2, 38 | } BufferDirection; 39 | 40 | typedef enum { 41 | IpcCommandType_Invalid = 0, 42 | IpcCommandType_LegacyRequest = 1, 43 | IpcCommandType_Close = 2, 44 | IpcCommandType_LegacyControl = 3, 45 | IpcCommandType_Request = 4, 46 | IpcCommandType_Control = 5, 47 | IpcCommandType_RequestWithContext = 6, 48 | IpcCommandType_ControlWithContext = 7, 49 | } IpcCommandType; 50 | 51 | typedef enum { 52 | DomainMessageType_Invalid = 0, 53 | DomainMessageType_SendMessage = 1, 54 | DomainMessageType_Close = 2, 55 | } DomainMessageType; 56 | 57 | /// IPC domain message header. 58 | typedef struct { 59 | u8 Type; 60 | u8 NumObjectIds; 61 | u16 Length; 62 | u32 ThisObjectId; 63 | u32 Pad[2]; 64 | } DomainMessageHeader; 65 | 66 | typedef struct { 67 | size_t NumSend; // A 68 | size_t NumRecv; // B 69 | size_t NumExch; // W 70 | const void* Buffers[IPC_MAX_BUFFERS]; 71 | size_t BufferSizes[IPC_MAX_BUFFERS]; 72 | BufferType BufferTypes[IPC_MAX_BUFFERS]; 73 | 74 | size_t NumStaticIn; // X 75 | size_t NumStaticOut; // C 76 | const void* Statics[IPC_MAX_BUFFERS]; 77 | size_t StaticSizes[IPC_MAX_BUFFERS]; 78 | u8 StaticIndices[IPC_MAX_BUFFERS]; 79 | 80 | bool SendPid; 81 | size_t NumHandlesCopy; 82 | size_t NumHandlesMove; 83 | Handle Handles[IPC_MAX_OBJECTS]; 84 | 85 | size_t NumObjectIds; 86 | u32 ObjectIds[IPC_MAX_OBJECTS]; 87 | } IpcCommand; 88 | 89 | /** 90 | * @brief Initializes an IPC command structure. 91 | * @param cmd IPC command structure. 92 | */ 93 | static inline void ipcInitialize(IpcCommand* cmd) { 94 | *cmd = (IpcCommand){0}; 95 | } 96 | 97 | /// IPC buffer descriptor. 98 | typedef struct { 99 | u32 Size; ///< Size of the buffer. 100 | u32 Addr; ///< Lower 32-bits of the address of the buffer 101 | u32 Packed; ///< Packed data (including higher bits of the address) 102 | } IpcBufferDescriptor; 103 | 104 | /// IPC static send-buffer descriptor. 105 | typedef struct { 106 | u32 Packed; ///< Packed data (including higher bits of the address) 107 | u32 Addr; ///< Lower 32-bits of the address 108 | } IpcStaticSendDescriptor; 109 | 110 | /// IPC static receive-buffer descriptor. 111 | typedef struct { 112 | u32 Addr; ///< Lower 32-bits of the address of the buffer 113 | u32 Packed; ///< Packed data (including higher bits of the address) 114 | } IpcStaticRecvDescriptor; 115 | 116 | /** 117 | * @brief Adds a buffer to an IPC command structure. 118 | * @param cmd IPC command structure. 119 | * @param buffer Address of the buffer. 120 | * @param size Size of the buffer. 121 | * @param type Buffer type. 122 | */ 123 | static inline void ipcAddSendBuffer(IpcCommand* cmd, const void* buffer, size_t size, BufferType type) { 124 | size_t off = cmd->NumSend; 125 | cmd->Buffers[off] = buffer; 126 | cmd->BufferSizes[off] = size; 127 | cmd->BufferTypes[off] = type; 128 | cmd->NumSend++; 129 | } 130 | 131 | /** 132 | * @brief Adds a receive-buffer to an IPC command structure. 133 | * @param cmd IPC command structure. 134 | * @param buffer Address of the buffer. 135 | * @param size Size of the buffer. 136 | * @param type Buffer type. 137 | */ 138 | static inline void ipcAddRecvBuffer(IpcCommand* cmd, void* buffer, size_t size, BufferType type) { 139 | size_t off = cmd->NumSend + cmd->NumRecv; 140 | cmd->Buffers[off] = buffer; 141 | cmd->BufferSizes[off] = size; 142 | cmd->BufferTypes[off] = type; 143 | cmd->NumRecv++; 144 | } 145 | 146 | /** 147 | * @brief Adds an exchange-buffer to an IPC command structure. 148 | * @param cmd IPC command structure. 149 | * @param buffer Address of the buffer. 150 | * @param size Size of the buffer. 151 | * @param type Buffer type. 152 | */ 153 | static inline void ipcAddExchBuffer(IpcCommand* cmd, void* buffer, size_t size, BufferType type) { 154 | size_t off = cmd->NumSend + cmd->NumRecv + cmd->NumExch; 155 | cmd->Buffers[off] = buffer; 156 | cmd->BufferSizes[off] = size; 157 | cmd->BufferTypes[off] = type; 158 | cmd->NumExch++; 159 | } 160 | 161 | /** 162 | * @brief Adds a static-buffer to an IPC command structure. 163 | * @param cmd IPC command structure. 164 | * @param buffer Address of the buffer. 165 | * @param size Size of the buffer. 166 | * @param index Index of buffer. 167 | */ 168 | static inline void ipcAddSendStatic(IpcCommand* cmd, const void* buffer, size_t size, u8 index) { 169 | size_t off = cmd->NumStaticIn; 170 | cmd->Statics[off] = buffer; 171 | cmd->StaticSizes[off] = size; 172 | cmd->StaticIndices[off] = index; 173 | cmd->NumStaticIn++; 174 | } 175 | 176 | /** 177 | * @brief Adds a static-receive-buffer to an IPC command structure. 178 | * @param cmd IPC command structure. 179 | * @param buffer Address of the buffer. 180 | * @param size Size of the buffer. 181 | * @param index Index of buffer. 182 | */ 183 | static inline void ipcAddRecvStatic(IpcCommand* cmd, void* buffer, size_t size, u8 index) { 184 | size_t off = cmd->NumStaticIn + cmd->NumStaticOut; 185 | cmd->Statics[off] = buffer; 186 | cmd->StaticSizes[off] = size; 187 | cmd->StaticIndices[off] = index; 188 | cmd->NumStaticOut++; 189 | } 190 | 191 | /** 192 | * @brief Adds a smart-buffer (buffer + static-buffer pair) to an IPC command structure. 193 | * @param cmd IPC command structure. 194 | * @param ipc_buffer_size IPC buffer size. 195 | * @param buffer Address of the buffer. 196 | * @param size Size of the buffer. 197 | * @param index Index of buffer. 198 | */ 199 | static inline void ipcAddSendSmart(IpcCommand* cmd, size_t ipc_buffer_size, const void* buffer, size_t size, u8 index) { 200 | if (ipc_buffer_size != 0 && size <= ipc_buffer_size) { 201 | ipcAddSendBuffer(cmd, NULL, 0, BufferType_Normal); 202 | ipcAddSendStatic(cmd, buffer, size, index); 203 | } else { 204 | ipcAddSendBuffer(cmd, buffer, size, BufferType_Normal); 205 | ipcAddSendStatic(cmd, NULL, 0, index); 206 | } 207 | } 208 | 209 | /** 210 | * @brief Adds a smart-receive-buffer (buffer + static-receive-buffer pair) to an IPC command structure. 211 | * @param cmd IPC command structure. 212 | * @param ipc_buffer_size IPC buffer size. 213 | * @param buffer Address of the buffer. 214 | * @param size Size of the buffer. 215 | * @param index Index of buffer. 216 | */ 217 | static inline void ipcAddRecvSmart(IpcCommand* cmd, size_t ipc_buffer_size, void* buffer, size_t size, u8 index) { 218 | if (ipc_buffer_size != 0 && size <= ipc_buffer_size) { 219 | ipcAddRecvBuffer(cmd, NULL, 0, BufferType_Normal); 220 | ipcAddRecvStatic(cmd, buffer, size, index); 221 | } else { 222 | ipcAddRecvBuffer(cmd, buffer, size, BufferType_Normal); 223 | ipcAddRecvStatic(cmd, NULL, 0, index); 224 | } 225 | } 226 | 227 | /** 228 | * @brief Tags an IPC command structure to send the PID. 229 | * @param cmd IPC command structure. 230 | */ 231 | static inline void ipcSendPid(IpcCommand* cmd) { 232 | cmd->SendPid = true; 233 | } 234 | 235 | /** 236 | * @brief Adds a copy-handle to be sent through an IPC command structure. 237 | * @param cmd IPC command structure. 238 | * @param h Handle to send. 239 | * @remark The receiving process gets a copy of the handle. 240 | */ 241 | static inline void ipcSendHandleCopy(IpcCommand* cmd, Handle h) { 242 | cmd->Handles[cmd->NumHandlesCopy++] = h; 243 | } 244 | 245 | /** 246 | * @brief Adds a move-handle to be sent through an IPC command structure. 247 | * @param cmd IPC command structure. 248 | * @param h Handle to send. 249 | * @remark The sending process loses ownership of the handle, which is transferred to the receiving process. 250 | */ 251 | static inline void ipcSendHandleMove(IpcCommand* cmd, Handle h) { 252 | cmd->Handles[cmd->NumHandlesCopy + cmd->NumHandlesMove++] = h; 253 | } 254 | 255 | /** 256 | * @brief Prepares the header of an IPC command structure. 257 | * @param cmd IPC command structure. 258 | * @param sizeof_raw Size in bytes of the raw data structure to embed inside the IPC request 259 | * @return Pointer to the raw embedded data structure in the request, ready to be filled out. 260 | */ 261 | static inline void* ipcPrepareHeader(IpcCommand* cmd, size_t sizeof_raw) { 262 | u32* buf = (u32*)armGetTls(); 263 | size_t i; 264 | *buf++ = IpcCommandType_Request | (cmd->NumStaticIn << 16) | (cmd->NumSend << 20) | (cmd->NumRecv << 24) | (cmd->NumExch << 28); 265 | 266 | u32* fill_in_size_later = buf; 267 | 268 | if (cmd->NumStaticOut > 0) { 269 | *buf = (cmd->NumStaticOut + 2) << 10; 270 | } 271 | else { 272 | *buf = 0; 273 | } 274 | 275 | if (cmd->SendPid || cmd->NumHandlesCopy > 0 || cmd->NumHandlesMove > 0) { 276 | *buf++ |= 0x80000000; 277 | *buf++ = (!!cmd->SendPid) | (cmd->NumHandlesCopy << 1) | (cmd->NumHandlesMove << 5); 278 | 279 | if (cmd->SendPid) 280 | buf += 2; 281 | 282 | for (i=0; i<(cmd->NumHandlesCopy + cmd->NumHandlesMove); i++) 283 | *buf++ = cmd->Handles[i]; 284 | } 285 | else { 286 | buf++; 287 | } 288 | 289 | for (i=0; iNumStaticIn; i++, buf+=2) { 290 | IpcStaticSendDescriptor* desc = (IpcStaticSendDescriptor*) buf; 291 | 292 | uintptr_t ptr = (uintptr_t) cmd->Statics[i]; 293 | desc->Addr = ptr; 294 | desc->Packed = cmd->StaticIndices[i] | (cmd->StaticSizes[i] << 16) | 295 | (((ptr >> 32) & 15) << 12) | (((ptr >> 36) & 15) << 6); 296 | } 297 | 298 | for (i=0; i<(cmd->NumSend + cmd->NumRecv + cmd->NumExch); i++, buf+=3) { 299 | IpcBufferDescriptor* desc = (IpcBufferDescriptor*) buf; 300 | desc->Size = cmd->BufferSizes[i]; 301 | 302 | uintptr_t ptr = (uintptr_t) cmd->Buffers[i]; 303 | desc->Addr = ptr; 304 | desc->Packed = cmd->BufferTypes[i] | 305 | (((ptr >> 32) & 15) << 28) | ((ptr >> 36) << 2); 306 | } 307 | 308 | u32 padding = ((16 - (((uintptr_t) buf) & 15)) & 15) / 4; 309 | u32* raw = (u32*) (buf + padding); 310 | 311 | size_t raw_size = (sizeof_raw/4) + 4; 312 | buf += raw_size; 313 | 314 | u16* buf_u16 = (u16*) buf; 315 | 316 | for (i=0; iNumStaticOut; i++) { 317 | size_t off = cmd->NumStaticIn + i; 318 | size_t sz = (uintptr_t) cmd->StaticSizes[off]; 319 | 320 | buf_u16[i] = (sz > 0xFFFF) ? 0 : sz; 321 | } 322 | 323 | size_t u16s_size = ((2*cmd->NumStaticOut) + 3)/4; 324 | buf += u16s_size; 325 | raw_size += u16s_size; 326 | 327 | *fill_in_size_later |= raw_size; 328 | 329 | for (i=0; iNumStaticOut; i++, buf+=2) { 330 | IpcStaticRecvDescriptor* desc = (IpcStaticRecvDescriptor*) buf; 331 | size_t off = cmd->NumStaticIn + i; 332 | 333 | uintptr_t ptr = (uintptr_t) cmd->Statics[off]; 334 | desc->Addr = ptr; 335 | desc->Packed = (ptr >> 32) | (cmd->StaticSizes[off] << 16); 336 | } 337 | 338 | return (void*) raw; 339 | } 340 | 341 | /** 342 | * @brief Dispatches an IPC request. 343 | * @param session IPC session handle. 344 | * @return Result code. 345 | */ 346 | static inline Result ipcDispatch(Handle session) { 347 | return svcSendSyncRequest(session); 348 | } 349 | 350 | ///@} 351 | 352 | ///@name IPC response parsing 353 | ///@{ 354 | 355 | /// IPC parsed command (response) structure. 356 | typedef struct { 357 | IpcCommandType CommandType; ///< Type of the command 358 | 359 | bool HasPid; ///< true if the 'Pid' field is filled out. 360 | u64 Pid; ///< PID included in the response (only if HasPid is true) 361 | 362 | size_t NumHandles; ///< Number of handles copied. 363 | Handle Handles[IPC_MAX_OBJECTS]; ///< Handles. 364 | bool WasHandleCopied[IPC_MAX_OBJECTS]; ///< true if the handle was moved, false if it was copied. 365 | 366 | bool IsDomainMessage; ///< true if the the message is a Domain message. 367 | DomainMessageType MessageType; ///< Type of the domain message. 368 | u32 MessageLength; ///< Size of rawdata (for domain messages). 369 | u32 ThisObjectId; ///< Object ID to call the command on (for domain messages). 370 | size_t NumObjectIds; ///< Number of object IDs (for domain messages). 371 | u32 ObjectIds[IPC_MAX_OBJECTS]; ///< Object IDs (for domain messages). 372 | 373 | size_t NumBuffers; ///< Number of buffers in the response. 374 | void* Buffers[IPC_MAX_BUFFERS]; ///< Pointers to the buffers. 375 | size_t BufferSizes[IPC_MAX_BUFFERS]; ///< Sizes of the buffers. 376 | BufferType BufferTypes[IPC_MAX_BUFFERS]; ///< Types of the buffers. 377 | BufferDirection BufferDirections[IPC_MAX_BUFFERS]; ///< Direction of each buffer. 378 | 379 | size_t NumStatics; ///< Number of statics in the response. 380 | void* Statics[IPC_MAX_BUFFERS]; ///< Pointers to the statics. 381 | size_t StaticSizes[IPC_MAX_BUFFERS]; ///< Sizes of the statics. 382 | u8 StaticIndices[IPC_MAX_BUFFERS]; ///< Indices of the statics. 383 | 384 | size_t NumStaticsOut; ///< Number of output statics available in the response. 385 | 386 | void* Raw; ///< Pointer to the raw embedded data structure in the response. 387 | void* RawWithoutPadding; ///< Pointer to the raw embedded data structure, without padding. 388 | size_t RawSize; ///< Size of the raw embedded data. 389 | } IpcParsedCommand; 390 | 391 | /** 392 | * @brief Parse an IPC command response into an IPC parsed command structure. 393 | * @param IPC parsed command structure to fill in. 394 | * @return Result code. 395 | */ 396 | static inline Result ipcParse(IpcParsedCommand* r) { 397 | u32* buf = (u32*)armGetTls(); 398 | u32 ctrl0 = *buf++; 399 | u32 ctrl1 = *buf++; 400 | size_t i; 401 | 402 | r->IsDomainMessage = false; 403 | 404 | r->CommandType = (IpcCommandType) (ctrl0 & 0xffff); 405 | r->HasPid = false; 406 | r->RawSize = (ctrl1 & 0x1ff) * 4; 407 | r->NumHandles = 0; 408 | 409 | r->NumStaticsOut = (ctrl1 >> 10) & 15; 410 | if (r->NumStaticsOut >> 1) r->NumStaticsOut--; // Value 2 -> Single descriptor 411 | if (r->NumStaticsOut >> 1) r->NumStaticsOut--; // Value 3+ -> (Value - 2) descriptors 412 | 413 | if (ctrl1 & 0x80000000) { 414 | u32 ctrl2 = *buf++; 415 | 416 | if (ctrl2 & 1) { 417 | r->HasPid = true; 418 | r->Pid = *buf++; 419 | r->Pid |= ((u64)(*buf++)) << 32; 420 | } 421 | 422 | size_t num_handles_copy = ((ctrl2 >> 1) & 15); 423 | size_t num_handles_move = ((ctrl2 >> 5) & 15); 424 | 425 | size_t num_handles = num_handles_copy + num_handles_move; 426 | u32* buf_after_handles = buf + num_handles; 427 | 428 | if (num_handles > IPC_MAX_OBJECTS) 429 | num_handles = IPC_MAX_OBJECTS; 430 | 431 | for (i=0; iHandles[i] = *(buf+i); 434 | r->WasHandleCopied[i] = (i < num_handles_copy); 435 | } 436 | 437 | r->NumHandles = num_handles; 438 | buf = buf_after_handles; 439 | } 440 | 441 | size_t num_statics = (ctrl0 >> 16) & 15; 442 | u32* buf_after_statics = buf + num_statics*2; 443 | 444 | if (num_statics > IPC_MAX_BUFFERS) 445 | num_statics = IPC_MAX_BUFFERS; 446 | 447 | for (i=0; iPacked; 450 | 451 | r->Statics[i] = (void*) (desc->Addr | (((packed >> 12) & 15) << 32) | (((packed >> 6) & 15) << 36)); 452 | r->StaticSizes[i] = packed >> 16; 453 | r->StaticIndices[i] = packed & 63; 454 | } 455 | 456 | r->NumStatics = num_statics; 457 | buf = buf_after_statics; 458 | 459 | size_t num_bufs_send = (ctrl0 >> 20) & 15; 460 | size_t num_bufs_recv = (ctrl0 >> 24) & 15; 461 | size_t num_bufs_exch = (ctrl0 >> 28) & 15; 462 | 463 | size_t num_bufs = num_bufs_send + num_bufs_recv + num_bufs_exch; 464 | r->Raw = (void*)(((uintptr_t)(buf + num_bufs*3) + 15) &~ 15); 465 | r->RawWithoutPadding = (void*)((uintptr_t)(buf + num_bufs*3)); 466 | 467 | if (num_bufs > IPC_MAX_BUFFERS) 468 | num_bufs = IPC_MAX_BUFFERS; 469 | 470 | for (i=0; iPacked; 473 | 474 | r->Buffers[i] = (void*) (desc->Addr | ((packed >> 28) << 32) | (((packed >> 2) & 15) << 36)); 475 | r->BufferSizes[i] = desc->Size; 476 | r->BufferTypes[i] = (BufferType) (packed & 3); 477 | 478 | if (i < num_bufs_send) 479 | r->BufferDirections[i] = BufferDirection_Send; 480 | else if (i < (num_bufs_send + num_bufs_recv)) 481 | r->BufferDirections[i] = BufferDirection_Recv; 482 | else 483 | r->BufferDirections[i] = BufferDirection_Exch; 484 | } 485 | 486 | r->NumBuffers = num_bufs; 487 | return 0; 488 | } 489 | 490 | /** 491 | * @brief Queries the size of an IPC pointer buffer. 492 | * @param session IPC session handle. 493 | * @param size Output variable in which to store the size. 494 | * @return Result code. 495 | */ 496 | static inline Result ipcQueryPointerBufferSize(Handle session, size_t *size) { 497 | u32* buf = (u32*)armGetTls(); 498 | 499 | buf[0] = IpcCommandType_Control; 500 | buf[1] = 8; 501 | buf[2] = 0; 502 | buf[3] = 0; 503 | buf[4] = SFCI_MAGIC; 504 | buf[5] = 0; 505 | buf[6] = 3; 506 | buf[7] = 0; 507 | 508 | Result rc = ipcDispatch(session); 509 | 510 | if (R_SUCCEEDED(rc)) { 511 | IpcParsedCommand r; 512 | ipcParse(&r); 513 | 514 | struct ipcQueryPointerBufferSizeResponse { 515 | u64 magic; 516 | u64 result; 517 | u32 size; 518 | } *raw = (struct ipcQueryPointerBufferSizeResponse*)r.Raw; 519 | 520 | rc = raw->result; 521 | 522 | if (R_SUCCEEDED(rc)) { 523 | *size = raw->size & 0xffff; 524 | } 525 | } 526 | 527 | return rc; 528 | } 529 | 530 | /** 531 | * @brief Closes the IPC session with proper clean up. 532 | * @param session IPC session handle. 533 | * @return Result code. 534 | */ 535 | static inline Result ipcCloseSession(Handle session) { 536 | u32* buf = (u32*)armGetTls(); 537 | buf[0] = IpcCommandType_Close; 538 | buf[1] = 0; 539 | return ipcDispatch(session); 540 | } 541 | ///@} 542 | 543 | ///@name IPC domain handling 544 | ///@{ 545 | 546 | /** 547 | * @brief Converts an IPC session handle into a domain. 548 | * @param session IPC session handle. 549 | * @param object_id_out Output variable in which to store the object ID. 550 | * @return Result code. 551 | */ 552 | static inline Result ipcConvertSessionToDomain(Handle session, u32* object_id_out) { 553 | u32* buf = (u32*)armGetTls(); 554 | 555 | buf[0] = IpcCommandType_Control; 556 | buf[1] = 8; 557 | buf[4] = SFCI_MAGIC; 558 | buf[5] = 0; 559 | buf[6] = 0; 560 | buf[7] = 0; 561 | 562 | Result rc = ipcDispatch(session); 563 | 564 | if (R_SUCCEEDED(rc)) { 565 | IpcParsedCommand r; 566 | ipcParse(&r); 567 | 568 | struct ipcConvertSessionToDomainResponse { 569 | u64 magic; 570 | u64 result; 571 | u32 object_id; 572 | } *raw = (struct ipcConvertSessionToDomainResponse*)r.Raw; 573 | 574 | rc = raw->result; 575 | 576 | if (R_SUCCEEDED(rc)) { 577 | *object_id_out = raw->object_id; 578 | } 579 | } 580 | 581 | return rc; 582 | } 583 | 584 | /** 585 | * @brief Adds an object ID to be sent through an IPC domain command structure. 586 | * @param cmd IPC domain command structure. 587 | * @param object_id Object ID to send. 588 | */ 589 | static inline void ipcSendObjectId(IpcCommand* cmd, u32 object_id) { 590 | cmd->ObjectIds[cmd->NumObjectIds++] = object_id; 591 | } 592 | 593 | /** 594 | * @brief Prepares the header of an IPC command structure (domain version). 595 | * @param cmd IPC command structure. 596 | * @param sizeof_raw Size in bytes of the raw data structure to embed inside the IPC request 597 | * @oaram object_id Domain object ID. 598 | * @return Pointer to the raw embedded data structure in the request, ready to be filled out. 599 | */ 600 | static inline void* ipcPrepareHeaderForDomain(IpcCommand* cmd, size_t sizeof_raw, u32 object_id) { 601 | void* raw = ipcPrepareHeader(cmd, sizeof_raw + sizeof(DomainMessageHeader)); 602 | DomainMessageHeader* hdr = (DomainMessageHeader*) raw; 603 | u32 *object_ids = (u32*)(((uintptr_t) raw) + sizeof(DomainMessageHeader) + sizeof_raw); 604 | 605 | hdr->Type = DomainMessageType_SendMessage; 606 | hdr->NumObjectIds = (u8)cmd->NumObjectIds; 607 | hdr->Length = sizeof_raw; 608 | hdr->ThisObjectId = object_id; 609 | hdr->Pad[0] = hdr->Pad[1] = 0; 610 | 611 | for(size_t i = 0; i < cmd->NumObjectIds; i++) 612 | object_ids[i] = cmd->ObjectIds[i]; 613 | return (void*)(((uintptr_t) raw) + sizeof(DomainMessageHeader)); 614 | } 615 | 616 | /** 617 | * @brief Parse an IPC command response into an IPC parsed command structure (domain version). 618 | * @param IPC parsed command structure to fill in. 619 | * @return Result code. 620 | */ 621 | static inline Result ipcParseForDomain(IpcParsedCommand* r) { 622 | Result rc = ipcParse(r); 623 | DomainMessageHeader *hdr; 624 | u32 *object_ids; 625 | if(R_FAILED(rc)) 626 | return rc; 627 | 628 | hdr = (DomainMessageHeader*) r->Raw; 629 | object_ids = (u32*)(((uintptr_t) hdr) + sizeof(DomainMessageHeader) + hdr->Length); 630 | r->Raw = (void*)(((uintptr_t) r->Raw) + sizeof(DomainMessageHeader)); 631 | 632 | r->IsDomainMessage = true; 633 | r->MessageType = (DomainMessageType)(hdr->Type); 634 | switch (r->MessageType) { 635 | case DomainMessageType_SendMessage: 636 | case DomainMessageType_Close: 637 | break; 638 | default: 639 | return MAKERESULT(Module_Libnx, LibnxError_DomainMessageUnknownType); 640 | } 641 | r->ThisObjectId = hdr->ThisObjectId; 642 | r->NumObjectIds = hdr->NumObjectIds > 8 ? 8 : hdr->NumObjectIds; 643 | if ((uintptr_t)object_ids + sizeof(u32) * r->NumObjectIds - (uintptr_t)armGetTls() >= 0x100) { 644 | return MAKERESULT(Module_Libnx, LibnxError_DomainMessageTooManyObjectIds); 645 | } 646 | for(size_t i = 0; i < r->NumObjectIds; i++) 647 | r->ObjectIds[i] = object_ids[i]; 648 | 649 | return rc; 650 | } 651 | 652 | /** 653 | * @brief Closes a domain object by ID. 654 | * @param session IPC session handle. 655 | * @param object_id ID of the object to close. 656 | * @return Result code. 657 | */ 658 | static inline Result ipcCloseObjectById(Handle session, u32 object_id) { 659 | IpcCommand c; 660 | DomainMessageHeader* hdr; 661 | 662 | ipcInitialize(&c); 663 | hdr = (DomainMessageHeader*)ipcPrepareHeader(&c, sizeof(DomainMessageHeader)); 664 | 665 | hdr->Type = 2; 666 | hdr->NumObjectIds = 0; 667 | hdr->Length = 0; 668 | hdr->ThisObjectId = object_id; 669 | hdr->Pad[0] = hdr->Pad[1] = 0; 670 | 671 | return ipcDispatch(session); // this command has no associated response 672 | } 673 | 674 | ///@} 675 | 676 | -------------------------------------------------------------------------------- /tests/unittests/data/teststaticnewlinebraces.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file condvar.h 3 | * @brief Condition variable synchronization primitive. 4 | * @author plutoo 5 | * @copyright libnx Authors 6 | */ 7 | #pragma once 8 | #include "../types.h" 9 | #include "../kernel/mutex.h" 10 | 11 | /// Condition variable structure. 12 | typedef struct { 13 | u32 tag; 14 | Mutex* mutex; 15 | } CondVar; 16 | 17 | /** 18 | * @brief Initializes a condition variable. 19 | * @param[in] c Condition variable object. 20 | * @param[in] m Mutex object to use inside the condition variable. 21 | */ 22 | void condvarInit(CondVar* c, Mutex* m); 23 | 24 | /** 25 | * @brief Waits on a condition variable with a timeout. 26 | * @param[in] c Condition variable object. 27 | * @param[in] timeout Timeout in nanoseconds. 28 | * @return Result code (0xEA01 on timeout). 29 | * @remark On function return, the underlying mutex is acquired. 30 | */ 31 | Result condvarWaitTimeout(CondVar* c, u64 timeout); 32 | 33 | /** 34 | * @brief Waits on a condition variable. 35 | * @param[in] c Condition variable object. 36 | * @return Result code. 37 | * @remark On function return, the underlying mutex is acquired. 38 | */ 39 | static inline Result condvarWait(CondVar* c) 40 | { 41 | return condvarWaitTimeout(c, -1ull); 42 | } 43 | 44 | /** 45 | * @brief Wakes up up to the specified number of threads waiting on a condition variable. 46 | * @param[in] c Condition variable object. 47 | * @param[in] num Maximum number of threads to wake up (or -1 to wake them all up). 48 | * @return Result code. 49 | */ 50 | Result condvarWake(CondVar* c, int num); 51 | 52 | /** 53 | * @brief Wakes up a single thread waiting on a condition variable. 54 | * @param[in] c Condition variable object. 55 | * @return Result code. 56 | */ 57 | static inline Result condvarWakeOne(CondVar* c) 58 | { 59 | return condvarWake(c, 1); 60 | } 61 | 62 | /** 63 | * @brief Wakes up all thread waiting on a condition variable. 64 | * @param[in] c Condition variable object. 65 | * @return Result code. 66 | */ 67 | static inline Result condvarWakeAll(CondVar* c) 68 | { 69 | return condvarWake(c, -1); 70 | } 71 | 72 | -------------------------------------------------------------------------------- /tests/unittests/data/teststaticnewlinebracesreadded.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file condvar.h 3 | * @brief Condition variable synchronization primitive. 4 | * @author plutoo 5 | * @copyright libnx Authors 6 | */ 7 | #pragma once 8 | #include "../types.h" 9 | #include "../kernel/mutex.h" 10 | 11 | /// Condition variable structure. 12 | typedef struct { 13 | u32 tag; 14 | Mutex* mutex; 15 | } CondVar; 16 | 17 | /** 18 | * @brief Initializes a condition variable. 19 | * @param[in] c Condition variable object. 20 | * @param[in] m Mutex object to use inside the condition variable. 21 | */ 22 | void condvarInit(CondVar* c, Mutex* m); 23 | 24 | /** 25 | * @brief Waits on a condition variable with a timeout. 26 | * @param[in] c Condition variable object. 27 | * @param[in] timeout Timeout in nanoseconds. 28 | * @return Result code (0xEA01 on timeout). 29 | * @remark On function return, the underlying mutex is acquired. 30 | */ 31 | Result condvarWaitTimeout(CondVar* c, u64 timeout); 32 | 33 | /** 34 | * @brief Waits on a condition variable. 35 | * @param[in] c Condition variable object. 36 | * @return Result code. 37 | * @remark On function return, the underlying mutex is acquired. 38 | */ 39 | static inline Result condvarWait(CondVar* c) { 40 | return condvarWaitTimeout(c, -1ull); 41 | } 42 | 43 | /** 44 | * @brief Wakes up up to the specified number of threads waiting on a condition variable. 45 | * @param[in] c Condition variable object. 46 | * @param[in] num Maximum number of threads to wake up (or -1 to wake them all up). 47 | * @return Result code. 48 | */ 49 | Result condvarWake(CondVar* c, int num); 50 | 51 | /** 52 | * @brief Wakes up a single thread waiting on a condition variable. 53 | * @param[in] c Condition variable object. 54 | * @return Result code. 55 | */ 56 | static inline Result condvarWakeOne(CondVar* c) { 57 | return condvarWake(c, 1); 58 | } 59 | 60 | /** 61 | * @brief Wakes up all thread waiting on a condition variable. 62 | * @param[in] c Condition variable object. 63 | * @return Result code. 64 | */ 65 | static inline Result condvarWakeAll(CondVar* c) { 66 | return condvarWake(c, -1); 67 | } 68 | 69 | -------------------------------------------------------------------------------- /tests/unittests/nim.cfg: -------------------------------------------------------------------------------- 1 | --path="../../src" 2 | -------------------------------------------------------------------------------- /tests/unittests/testfileops.nim: -------------------------------------------------------------------------------- 1 | import nimgen/fileops, common, regex, os 2 | 3 | import unittest 4 | 5 | let testFileContent = """ 6 | this is text 7 | this is text 8 | replace me 9 | prepend me 10 | end 11 | """ 12 | 13 | let prependMiddleExpected = """ 14 | this is text 15 | this is text 16 | replace me 17 | prepended data 18 | prepend me 19 | end 20 | """ 21 | 22 | let prependEndExpected = """ 23 | this is text 24 | this is text 25 | replace me 26 | prepend me 27 | data 28 | end 29 | """ 30 | 31 | let appendEndExpected = """ 32 | this is text 33 | this is text 34 | replace me 35 | prepend me 36 | end 37 | data 38 | """ 39 | 40 | let appendMiddleExpected = """ 41 | this is data 42 | text 43 | this is text 44 | replace me 45 | prepend me 46 | end 47 | """ 48 | 49 | let freplaceDefaultExpected = """ 50 | 51 | 52 | replace me 53 | prepend me 54 | end 55 | """ 56 | 57 | let freplaceWithExpected = """ 58 | this is text 59 | this is text 60 | foobar 61 | prepend me 62 | end 63 | """ 64 | 65 | let freplaceRegexExpected = """ 66 | foobar 67 | foobar 68 | replace me 69 | prepend me 70 | end 71 | """ 72 | 73 | let commentExpected = """ 74 | this is text 75 | this is text 76 | //replace me 77 | //prepend me 78 | //end 79 | """ 80 | 81 | let commentMiddleExpected = """ 82 | this //is text 83 | //this is text 84 | replace me 85 | prepend me 86 | end 87 | """ 88 | 89 | 90 | let dataDir = currentSourcePath().splitPath().head / "data" 91 | 92 | let testfilename = dataDir / "testing.txt" 93 | 94 | 95 | suite "test file ops": 96 | if not dataDir.dirExists(): 97 | dataDir.createDir() 98 | 99 | setup: 100 | writeFile(testfilename, testFileContent) 101 | 102 | ################### Prepend ####################### 103 | 104 | test "prepend at beginning of file": 105 | prepend(testfilename, "data\n") 106 | let expected = "data\n" & testFileContent 107 | testfilename.checkFile(expected) 108 | 109 | test "prepend at middle of file": 110 | prepend(testfilename, "prepended data\n", "prepend me") 111 | testfilename.checkFile(prependMiddleExpected) 112 | 113 | test "prepend at end of file": 114 | prepend(testfilename, "data\n", "end\n") 115 | testfilename.checkFile(prependEndExpected) 116 | 117 | ################### Pipe ######################### 118 | 119 | test "pipe command into file": 120 | when defined(windows): 121 | pipe(testfilename, "ECHO foo") 122 | testfilename.checkFile("foo") 123 | else: 124 | pipe(testfilename, "cat $file | grep 'this is text'") 125 | testfilename.checkFile("this is text\nthis is text") 126 | 127 | ################# Append ######################### 128 | 129 | test "append file end": 130 | append(testfilename, "data\n") 131 | testfilename.checkFile(appendEndExpected) 132 | 133 | test "append file middle": 134 | append(testfilename, " data\n", "this is") 135 | testfilename.checkFile(appendMiddleExpected) 136 | 137 | ################# FReplace ######################### 138 | 139 | test "freplace default empty": 140 | freplace(testfilename, "this is text") 141 | testfilename.checkFile(freplaceDefaultExpected) 142 | 143 | test "freplace with content": 144 | freplace(testfilename, "replace me", "foobar") 145 | testfilename.checkFile(freplaceWithExpected) 146 | 147 | test "freplace regex": 148 | freplace(testfilename, re"this .*", "foobar") 149 | testfilename.checkFile(freplaceRegexExpected) 150 | 151 | ####################### Comment ###################### 152 | 153 | test "comment": 154 | comment(testfilename, "replace me", "3") 155 | testfilename.checkFile(commentExpected) 156 | 157 | test "comment over length": 158 | comment(testfilename, "replace me", "10") 159 | testfilename.checkFile(commentExpected) 160 | 161 | test "comment negative": 162 | comment(testfilename, "replace me", "-3") 163 | testfilename.checkFile(testFileContent) 164 | 165 | test "comment zero": 166 | comment(testfilename, "replace me", "0") 167 | testfilename.checkFile(testFileContent) 168 | 169 | test "comment middle": 170 | comment(testfilename, "is text", "2") 171 | testfilename.checkFile(commentMiddleExpected) 172 | 173 | ############### Static inline removal ################ 174 | 175 | test "replace static inline with front braces at end of line": 176 | 177 | let 178 | file = dataDir / "teststaticfrontbraces.h" 179 | resFile = dataDir / "teststaticexpectedfrontbraces.h" 180 | 181 | test = readFile(file) 182 | expected = readFile(resFile) 183 | 184 | writeFile(testfilename, test) 185 | 186 | removeStatic(testfilename) 187 | testfilename.checkFile(expected) 188 | 189 | reAddStatic(testfilename) 190 | testfilename.checkFile(test) 191 | 192 | test "replace static inline with newline before brace": 193 | 194 | let 195 | file = dataDir / "teststaticnewlinebraces.h" 196 | resFile = dataDir / "teststaticexpectednewlinebraces.h" 197 | reAddedFile = dataDir / "teststaticnewlinebracesreadded.h" 198 | 199 | test = readFile(file) 200 | expected = readFile(resFile) 201 | reAdded = readFile(reAddedFile) 202 | 203 | writeFile(testfilename, test) 204 | 205 | removeStatic(testfilename) 206 | testfilename.checkFile(expected) 207 | 208 | reAddStatic(testfilename) 209 | testfilename.checkFile(reAdded) 210 | -------------------------------------------------------------------------------- /web/CNAME: -------------------------------------------------------------------------------- 1 | nimgen.genotrance.com -------------------------------------------------------------------------------- /web/nimdoc.cfg: -------------------------------------------------------------------------------- 1 | # This is the config file for the documentation generator. 2 | # (c) 2016 Andreas Rumpf 3 | # Feel free to edit the templates as you need. If you modify this file, it 4 | # might be worth updating the hardcoded values in packages/docutils/rstgen.nim 5 | 6 | split.item.toc = "20" 7 | # too long entries in the table of contents wrap around 8 | # after this number of characters 9 | 10 | doc.section = """ 11 |
12 |

$sectionTitle

13 |
14 | $content 15 |
16 | """ 17 | 18 | doc.section.toc = """ 19 |
  • 20 | $sectionTitle 21 |
      22 | $content 23 |
    24 |
  • 25 | """ 26 | 27 | # Chunk of HTML emitted for each entry in the HTML table of contents. 28 | # Available variables are: 29 | # * $desc: the actual docstring of the item. 30 | # * $header: the full version of name, including types, pragmas, tags, etc. 31 | # * $header_plain: like header but without HTML, for attribute embedding. 32 | # * $itemID: numerical unique entry of the item in the HTML. 33 | # * $itemSym: short symbolic name of the item for easier hyperlinking. 34 | # * $itemSymEnc: quoted version for URLs or attributes. 35 | # * $itemSymOrID: the symbolic name or the ID if that is not unique. 36 | # * $itemSymOrIDEnc: quoted version for URLs or attributes. 37 | # * $name: reduced name of the item. 38 | # * $seeSrc: generated HTML from doc.item.seesrc (if some switches are used). 39 | 40 | doc.item = """ 41 |
    $header
    42 |
    43 | $desc 44 | $seeSrc 45 |
    46 | """ 47 | 48 | # Chunk of HTML emitted for each entry in the HTML table of contents. 49 | # See doc.item for available substitution variables. 50 | doc.item.toc = """ 51 |
  • $name
  • 53 | """ 54 | 55 | # HTML rendered for doc.item's seeSrc variable. Note that this will render to 56 | # the empty string if you don't pass anything through --docSeeSrcURL. Available 57 | # substitutaion variables here are: 58 | # * $path: relative path to the file being processed. 59 | # * $line: line of the item in the original source file. 60 | # * $url: whatever you did pass through the --docSeeSrcUrl switch (which also 61 | # gets variables path/line replaced!) 62 | doc.item.seesrc = """  Source 65 | """ 66 | 67 | doc.toc = """ 68 |
      69 | $content 70 |
    71 | """ 72 | 73 | doc.body_toc = """ 74 |
    75 |
    76 |
    77 | Search: 79 |
    80 | $tableofcontents 81 |
    82 |
    83 |
    84 |

    $moduledesc

    85 | $content 86 |
    87 |
    88 | """ 89 | 90 | @if boot: 91 | # This is enabled with the "boot" directive to generate 92 | # the compiler documentation. 93 | # As a user, tweak the block below instead. 94 | # You can add your own global-links entries 95 | doc.body_toc_group = """ 96 |
    97 |
    98 | 111 |
    112 | Search: 114 |
    115 |
    116 | Group by: 117 | 121 |
    122 | $tableofcontents 123 |
    124 |
    125 |
    126 |

    $moduledesc

    127 | $content 128 |
    129 |
    130 | """ 131 | 132 | @else 133 | 134 | doc.body_toc_group = """ 135 |
    136 |
    137 | 141 |
    142 | Search: 144 |
    145 |
    146 | Group by: 147 | 151 |
    152 | $tableofcontents 153 |
    154 |
    155 |
    156 |

    $moduledesc

    157 | $content 158 |
    159 |
    160 | """ 161 | @end 162 | 163 | doc.body_no_toc = """ 164 | $moduledesc 165 | $content 166 | """ 167 | 168 | doc.listing_start = "
    "
     169 | doc.listing_end = "
    " 170 | 171 | # * $analytics: Google analytics location, includes 1356 | 1357 | 1382 | 1383 | 1384 | 1385 |
    1386 |
    1387 |

    $title

    1388 | $content 1389 |
    1390 | 1395 |
    1396 |
    1397 |
    1398 | $analytics 1399 | 1400 | 1401 | """ 1402 | --------------------------------------------------------------------------------