├── .clang-format ├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── build ├── msvc │ ├── build.default.props │ ├── task.sln │ ├── task.vcxproj │ └── test │ │ ├── all.vcxproj │ │ └── task.vcxproj ├── ninja │ ├── android.py │ ├── clang.py │ ├── codesign.py │ ├── gcc.py │ ├── generator.py │ ├── msvc.py │ ├── platform.py │ ├── plist.py │ ├── syntax.py │ ├── toolchain.py │ ├── version.py │ ├── vslocate.py │ └── xcode.py └── task.sublime-project ├── configure.py ├── task ├── build.h ├── executor.c ├── executor.h ├── fiber.c ├── fiber.h ├── hashstrings.h ├── hashstrings.txt ├── scheduler.c ├── scheduler.h ├── task.c ├── task.h ├── types.h └── version.c └── test ├── all ├── android │ ├── AndroidManifest.xml │ ├── drawable-hdpi │ │ └── icon.png │ ├── drawable-ldpi │ │ └── icon.png │ ├── drawable-mdpi │ │ └── icon.png │ ├── drawable-xhdpi │ │ └── icon.png │ ├── drawable-xxhdpi │ │ └── icon.png │ ├── drawable-xxxhdpi │ │ └── icon.png │ ├── java │ │ └── com │ │ │ └── maniccoder │ │ │ └── task │ │ │ └── test │ │ │ └── TestActivity.java │ ├── layout │ │ └── main.xml │ └── values │ │ └── strings.xml ├── ios │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── icon_100.png │ │ │ ├── icon_114.png │ │ │ ├── icon_120.png │ │ │ ├── icon_144.png │ │ │ ├── icon_152.png │ │ │ ├── icon_180.png │ │ │ ├── icon_29-1.png │ │ │ ├── icon_29.png │ │ │ ├── icon_40.png │ │ │ ├── icon_50.png │ │ │ ├── icon_57.png │ │ │ ├── icon_58-1.png │ │ │ ├── icon_58.png │ │ │ ├── icon_72.png │ │ │ ├── icon_76.png │ │ │ ├── icon_80-1.png │ │ │ └── icon_80.png │ │ └── LaunchImage.launchimage │ │ │ ├── Contents.json │ │ │ ├── launch_1024_748.png │ │ │ ├── launch_1024_768.png │ │ │ ├── launch_1536_2008.png │ │ │ ├── launch_1536_2048.png │ │ │ ├── launch_2048_1496.png │ │ │ ├── launch_2048_1536.png │ │ │ ├── launch_320_480.png │ │ │ ├── launch_640_1136.png │ │ │ ├── launch_640_960.png │ │ │ ├── launch_768_1004.png │ │ │ └── launch_768_1024.png │ ├── test-all.plist │ ├── test-all.xib │ ├── viewcontroller.h │ └── viewcontroller.m ├── main.c └── tizen │ ├── res │ └── tizenapp.png │ └── tizen-manifest.xml └── task └── main.c /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Google 2 | AlignAfterOpenBracket: Align 3 | AlignConsecutiveAssignments: 'false' 4 | AlignConsecutiveDeclarations: 'false' 5 | AlignOperands: 'true' 6 | AlignTrailingComments: 'true' 7 | AllowAllParametersOfDeclarationOnNextLine: 'false' 8 | AllowShortBlocksOnASingleLine: 'false' 9 | AllowShortCaseLabelsOnASingleLine: 'false' 10 | AllowShortFunctionsOnASingleLine: None 11 | AllowShortIfStatementsOnASingleLine: 'false' 12 | AllowShortLoopsOnASingleLine: 'false' 13 | AlwaysBreakAfterDefinitionReturnType: TopLevel 14 | AlwaysBreakAfterReturnType: TopLevel 15 | AlwaysBreakBeforeMultilineStrings: 'true' 16 | AlwaysBreakTemplateDeclarations: 'true' 17 | BinPackArguments: 'true' 18 | BinPackParameters: 'true' 19 | BreakBeforeBinaryOperators: None 20 | BreakBeforeBraces: Attach 21 | BreakBeforeTernaryOperators: 'false' 22 | ColumnLimit: '120' 23 | ConstructorInitializerAllOnOneLineOrOnePerLine: 'false' 24 | DerivePointerAlignment: 'false' 25 | ExperimentalAutoDetectBinPacking: 'false' 26 | IndentCaseLabels: 'true' 27 | IndentWidth: '4' 28 | IndentWrappedFunctionNames: 'false' 29 | KeepEmptyLinesAtTheStartOfBlocks: 'false' 30 | MaxEmptyLinesToKeep: '1' 31 | NamespaceIndentation: None 32 | ObjCSpaceAfterProperty: 'true' 33 | ObjCSpaceBeforeProtocolList: 'true' 34 | PointerAlignment: Left 35 | SortIncludes: 'false' 36 | SpaceAfterCStyleCast: 'false' 37 | SpaceAfterTemplateKeyword: 'true' 38 | SpaceBeforeAssignmentOperators: 'true' 39 | SpaceBeforeParens: ControlStatements 40 | SpaceInEmptyParentheses: 'false' 41 | SpacesInAngles: 'false' 42 | SpacesInCStyleCastParentheses: 'false' 43 | SpacesInContainerLiterals: 'false' 44 | SpacesInParentheses: 'false' 45 | SpacesInSquareBrackets: 'false' 46 | TabWidth: '4' 47 | UseTab: ForIndentation 48 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Never mangle resource blob files 5 | *.blob -text 6 | 7 | # Custom for Visual Studio 8 | *.cs diff=csharp 9 | *.sln merge=union 10 | *.csproj merge=union 11 | *.vbproj merge=union 12 | *.fsproj merge=union 13 | *.dbproj merge=union 14 | 15 | # Standard to msysgit 16 | *.doc diff=astextplain 17 | *.DOC diff=astextplain 18 | *.docx diff=astextplain 19 | *.DOCX diff=astextplain 20 | *.dot diff=astextplain 21 | *.DOT diff=astextplain 22 | *.pdf diff=astextplain 23 | *.PDF diff=astextplain 24 | *.rtf diff=astextplain 25 | *.RTF diff=astextplain 26 | 27 | # Ignore build scripts in language stats 28 | build/* linguist-vendored 29 | configure.py linguist-vendored=true 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pydevproject 2 | .project 3 | .metadata 4 | .gitconfig 5 | bin/ 6 | tmp/ 7 | *.tmp 8 | *.bak 9 | *.swp 10 | *~.nib 11 | local.properties 12 | .classpath 13 | .settings/ 14 | .loadpath 15 | .ninja* 16 | build.ninja 17 | 18 | # Generated version 19 | version.c 20 | 21 | # External tool builders 22 | .externalToolBuilders/ 23 | 24 | # Locally stored "Eclipse launch configurations" 25 | *.launch 26 | 27 | # CDT-specific 28 | .cproject 29 | 30 | # PDT-specific 31 | .buildpath 32 | 33 | #Android local build files 34 | build/android/assets 35 | build/android/libs 36 | 37 | #Xcode build 38 | build/xcode/foundation/build 39 | 40 | #Doxygen generated 41 | doc/html 42 | 43 | #Coverity scan 44 | cov-int/ 45 | cov-int.* 46 | 47 | ################# 48 | ## Visual Studio 49 | ################# 50 | 51 | ## Ignore Visual Studio temporary files, build results, and 52 | ## files generated by popular Visual Studio add-ons. 53 | 54 | # User-specific files 55 | *.suo 56 | *.user 57 | *.sln.docstates 58 | **/.vs 59 | 60 | # Build results 61 | [Dd]ebug/ 62 | [Rr]elease/ 63 | [Pp]rofile/ 64 | [Dd]eploy/ 65 | *_i.c 66 | *_p.c 67 | *.ilk 68 | *.meta 69 | *.obj 70 | *.pch 71 | *.pchi 72 | *.pdb 73 | *.pgc 74 | *.pgd 75 | *.rsp 76 | *.sbr 77 | *.tlb 78 | *.tli 79 | *.tlh 80 | *.tmp 81 | *.vspscc 82 | .builds 83 | *.dotCover 84 | *.lastbuildstate 85 | *.unsuccessfulbuild 86 | *.opendb 87 | *.vc 88 | *.VC.db 89 | *.db-shm 90 | *.db-wal 91 | 92 | ## TODO: If you have NuGet Package Restore enabled, uncomment this 93 | #packages/ 94 | 95 | # Visual C++ cache files 96 | ipch/ 97 | *.aps 98 | *.ncb 99 | *.opensdf 100 | *.sdf 101 | 102 | # Visual Studio profiler 103 | *.psess 104 | *.vsp 105 | 106 | # ReSharper is a .NET coding add-in 107 | _ReSharper* 108 | 109 | # Installshield output folder 110 | [Ee]xpress 111 | 112 | # DocProject is a documentation generator add-in 113 | DocProject/buildhelp/ 114 | DocProject/Help/*.HxT 115 | DocProject/Help/*.HxC 116 | DocProject/Help/*.hhc 117 | DocProject/Help/*.hhk 118 | DocProject/Help/*.hhp 119 | DocProject/Help/Html2 120 | DocProject/Help/html 121 | 122 | # Click-Once directory 123 | publish 124 | 125 | # Others 126 | [Bb]in 127 | [Oo]bj 128 | sql 129 | TestResults 130 | *.Cache 131 | ClientBin 132 | stylecop.* 133 | ~$* 134 | *.dbmdl 135 | Generated_Code #added for RIA/Silverlight projects 136 | 137 | # Backup & report files from converting an old project file to a newer 138 | # Visual Studio version. Backup files are not needed, because we have git ;-) 139 | _UpgradeReport_Files/ 140 | Backup*/ 141 | UpgradeLog*.XML 142 | 143 | 144 | ############ 145 | ## Windows 146 | ############ 147 | 148 | # Windows image file caches 149 | Thumbs.db 150 | 151 | # Folder config file 152 | Desktop.ini 153 | 154 | 155 | ############# 156 | ## Python 157 | ############# 158 | 159 | *.py[co] 160 | 161 | # Packages 162 | *.egg 163 | *.egg-info 164 | dist 165 | eggs 166 | parts 167 | var 168 | sdist 169 | develop-eggs 170 | .installed.cfg 171 | 172 | # Installer logs 173 | pip-log.txt 174 | 175 | # Unit test / coverage reports 176 | .coverage 177 | .tox 178 | 179 | #Translations 180 | *.mo 181 | 182 | #Mr Developer 183 | .mr.developer.cfg 184 | 185 | # Mac crap 186 | .DS_Store 187 | 188 | 189 | ############### 190 | ## Generic 191 | ############### 192 | 193 | #Project builds 194 | lib/** 195 | bin/** 196 | dist/** 197 | libs/** 198 | 199 | #Log files 200 | *.log 201 | *.tlog 202 | 203 | #Scons build 204 | .sconsign.dblite 205 | build/scons/debug* 206 | build/scons/release* 207 | build/scons/profi* 208 | build/scons/deploy* 209 | 210 | #Backups 211 | *~ 212 | 213 | #Object files 214 | *.o 215 | 216 | #XCode 217 | xcuserdata 218 | *.xccheckout 219 | 220 | #SublimeText local workspace 221 | *.sublime-workspace 222 | 223 | #Never store keystores in version control! 224 | *.keystore 225 | 226 | #Do not store local build prefs 227 | build.json 228 | codesign.json 229 | coveralls.json 230 | coverallsreport.json 231 | codecov.json 232 | codecovreport.json 233 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Task Library - Public Domain 2 | 3 | This library provides a cross-platform library in C11 providing 4 | task-based parallellism for projects based on our foundation library. 5 | 6 | The latest source code maintained by Mattias Jansson is always available at 7 | 8 | https://github.com/mjansson/task_lib 9 | 10 | The foundation library source code maintained by Mattias Jansson is always available at 11 | 12 | https://github.com/mjansson/foundation_lib 13 | 14 | This library is put in the public domain; you can redistribute it and/or modify it without any restrictions. 15 | 16 | -------------------------------------------------------------------------------- /build/msvc/build.default.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | x64 7 | 8 | 9 | Deploy 10 | x64 11 | 12 | 13 | Profile 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | $(Platform)\$(Configuration)\$(ProjectName)\ 23 | Unicode 24 | Sequential 25 | 26 | 27 | 28 | v143 29 | 30 | 31 | 32 | 33 | true 34 | false 35 | 36 | 37 | $(SolutionDir)..\..\bin\windows\debug\x86-64\ 38 | 39 | 40 | $(SolutionDir)..\..\lib\windows\debug\x86-64\ 41 | 42 | 43 | 44 | 45 | false 46 | true 47 | true 48 | 49 | 50 | $(SolutionDir)..\..\bin\windows\release\x86-64\ 51 | 52 | 53 | $(SolutionDir)..\..\lib\windows\release\x86-64\ 54 | 55 | 56 | 57 | 58 | false 59 | true 60 | true 61 | 62 | 63 | $(SolutionDir)..\..\bin\windows\deploy\x86-64\ 64 | 65 | 66 | $(SolutionDir)..\..\lib\windows\deploy\x86-64\ 67 | 68 | 69 | 70 | 71 | false 72 | true 73 | true 74 | 75 | 76 | $(SolutionDir)..\..\bin\windows\profile\x86-64\ 77 | 78 | 79 | $(SolutionDir)..\..\lib\windows\profile\x86-64\ 80 | 81 | 82 | 83 | 84 | false 85 | true 86 | true 87 | 88 | 89 | 90 | 91 | 92 | Level3 93 | $(ProjectDir)..\..;%(AdditionalIncludeDirectories) 94 | ProgramDatabase 95 | false 96 | false 97 | true 98 | true 99 | true 100 | false 101 | Default 102 | MultiThreaded 103 | false 104 | false 105 | AdvancedVectorExtensions2 106 | Fast 107 | false 108 | false 109 | false 110 | false 111 | false 112 | true 113 | SSE3 114 | true 115 | true 116 | true 117 | false 118 | stdc17 119 | 120 | 121 | Windows 122 | true 123 | 124 | 125 | 126 | 127 | opengl32.lib;gdi32.lib;iphlpapi.lib;ws2_32.lib;%(AdditionalDependencies) 128 | 129 | 130 | 131 | 132 | opengl32.lib;gdi32.lib;iphlpapi.lib;ws2_32.lib;%(AdditionalDependencies) 133 | 134 | 135 | 136 | 137 | BUILD_DEBUG=1;%(PreprocessorDefinitions) 138 | Disabled 139 | false 140 | false 141 | false 142 | 143 | 144 | $(SolutionDir)..\..\lib\windows;$(SolutionDir)..\..\lib\windows\debug\x86-64 145 | 146 | 147 | 148 | 149 | BUILD_RELEASE=1;%(PreprocessorDefinitions) 150 | ..\.. 151 | MaxSpeed 152 | AnySuitable 153 | Speed 154 | true 155 | true 156 | false 157 | 158 | 159 | true 160 | true 161 | $(SolutionDir)..\..\lib\windows;$(SolutionDir)..\..\lib\windows\release\x86-64 162 | 163 | 164 | 165 | 166 | $(ProjectDir)..\.. 167 | BUILD_DEPLOY=1;%(PreprocessorDefinitions) 168 | ..\.. 169 | Full 170 | AnySuitable 171 | Speed 172 | true 173 | true 174 | true 175 | 176 | 177 | true 178 | true 179 | $(SolutionDir)..\..\lib\windows;$(SolutionDir)..\..\lib\windows\deploy\x86-64 180 | 181 | 182 | 183 | 184 | $(ProjectDir)..\.. 185 | BUILD_PROFILE=1;%(PreprocessorDefinitions) 186 | Full 187 | AnySuitable 188 | Speed 189 | true 190 | true 191 | true 192 | 193 | 194 | true 195 | true 196 | $(SolutionDir)..\..\lib\windows;$(SolutionDir)..\..\lib\windows\profile\x86-64 197 | 198 | 199 | -------------------------------------------------------------------------------- /build/msvc/task.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31903.59 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "task", "task.vcxproj", "{74B0104C-EBE1-43AD-8C05-9BA418830A92}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{0BE9971B-A7EB-4B33-868E-BEDD2EC639A1}" 9 | EndProject 10 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "all", "test\all.vcxproj", "{793D1E99-B0BF-40E4-A5C3-947CD328787B}" 11 | EndProject 12 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "task", "test\task.vcxproj", "{8B1675E7-5B85-4389-926E-91177C2F0794}" 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|x64 = Debug|x64 17 | Deploy|x64 = Deploy|x64 18 | Profile|x64 = Profile|x64 19 | Release|x64 = Release|x64 20 | EndGlobalSection 21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 22 | {74B0104C-EBE1-43AD-8C05-9BA418830A92}.Debug|x64.ActiveCfg = Debug|x64 23 | {74B0104C-EBE1-43AD-8C05-9BA418830A92}.Debug|x64.Build.0 = Debug|x64 24 | {74B0104C-EBE1-43AD-8C05-9BA418830A92}.Deploy|x64.ActiveCfg = Deploy|x64 25 | {74B0104C-EBE1-43AD-8C05-9BA418830A92}.Deploy|x64.Build.0 = Deploy|x64 26 | {74B0104C-EBE1-43AD-8C05-9BA418830A92}.Profile|x64.ActiveCfg = Profile|x64 27 | {74B0104C-EBE1-43AD-8C05-9BA418830A92}.Profile|x64.Build.0 = Profile|x64 28 | {74B0104C-EBE1-43AD-8C05-9BA418830A92}.Release|x64.ActiveCfg = Release|x64 29 | {74B0104C-EBE1-43AD-8C05-9BA418830A92}.Release|x64.Build.0 = Release|x64 30 | {793D1E99-B0BF-40E4-A5C3-947CD328787B}.Debug|x64.ActiveCfg = Debug|x64 31 | {793D1E99-B0BF-40E4-A5C3-947CD328787B}.Debug|x64.Build.0 = Debug|x64 32 | {793D1E99-B0BF-40E4-A5C3-947CD328787B}.Deploy|x64.ActiveCfg = Deploy|x64 33 | {793D1E99-B0BF-40E4-A5C3-947CD328787B}.Deploy|x64.Build.0 = Deploy|x64 34 | {793D1E99-B0BF-40E4-A5C3-947CD328787B}.Profile|x64.ActiveCfg = Profile|x64 35 | {793D1E99-B0BF-40E4-A5C3-947CD328787B}.Profile|x64.Build.0 = Profile|x64 36 | {793D1E99-B0BF-40E4-A5C3-947CD328787B}.Release|x64.ActiveCfg = Release|x64 37 | {793D1E99-B0BF-40E4-A5C3-947CD328787B}.Release|x64.Build.0 = Release|x64 38 | {8B1675E7-5B85-4389-926E-91177C2F0794}.Debug|x64.ActiveCfg = Debug|x64 39 | {8B1675E7-5B85-4389-926E-91177C2F0794}.Debug|x64.Build.0 = Debug|x64 40 | {8B1675E7-5B85-4389-926E-91177C2F0794}.Deploy|x64.ActiveCfg = Deploy|x64 41 | {8B1675E7-5B85-4389-926E-91177C2F0794}.Deploy|x64.Build.0 = Deploy|x64 42 | {8B1675E7-5B85-4389-926E-91177C2F0794}.Profile|x64.ActiveCfg = Profile|x64 43 | {8B1675E7-5B85-4389-926E-91177C2F0794}.Profile|x64.Build.0 = Profile|x64 44 | {8B1675E7-5B85-4389-926E-91177C2F0794}.Release|x64.ActiveCfg = Release|x64 45 | {8B1675E7-5B85-4389-926E-91177C2F0794}.Release|x64.Build.0 = Release|x64 46 | EndGlobalSection 47 | GlobalSection(SolutionProperties) = preSolution 48 | HideSolutionNode = FALSE 49 | EndGlobalSection 50 | GlobalSection(NestedProjects) = preSolution 51 | {793D1E99-B0BF-40E4-A5C3-947CD328787B} = {0BE9971B-A7EB-4B33-868E-BEDD2EC639A1} 52 | {8B1675E7-5B85-4389-926E-91177C2F0794} = {0BE9971B-A7EB-4B33-868E-BEDD2EC639A1} 53 | EndGlobalSection 54 | GlobalSection(ExtensibilityGlobals) = postSolution 55 | SolutionGuid = {A890CD63-E921-4CED-8598-54FEDE9DBB04} 56 | EndGlobalSection 57 | EndGlobal 58 | -------------------------------------------------------------------------------- /build/msvc/task.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Win32Proj 5 | task 6 | 10.0 7 | StaticLibrary 8 | {74B0104C-EBE1-43AD-8C05-9BA418830A92} 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | $(ProjectDir)..\..\..\foundation;$(ProjectDir)..\..\..\foundation_lib;%(AdditionalIncludeDirectories) 38 | TASK_COMPILE=1;%(PreprocessorDefinitions) 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /build/msvc/test/all.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Win32Proj 5 | task 6 | 10.0 7 | Application 8 | {793D1E99-B0BF-40E4-A5C3-947CD328787B} 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | test-$(ProjectName) 21 | 22 | 23 | 24 | 25 | 26 | 27 | $(ProjectDir)..\..\..;$(ProjectDir)..\..\..\test;$(ProjectDir)..\..\..\..\foundation;$(ProjectDir)..\..\..\..\foundation_lib;$(ProjectDir)..\..\..\..\foundation\test;$(ProjectDir)..\..\..\..\foundation_lib\test;%(AdditionalIncludeDirectories) 28 | 29 | 30 | Console 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /build/msvc/test/task.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Win32Proj 5 | task 6 | 10.0 7 | Application 8 | {8B1675E7-5B85-4389-926E-91177C2F0794} 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | test-$(ProjectName) 21 | 22 | 23 | 24 | 25 | 26 | 27 | {74b0104c-ebe1-43ad-8c05-9ba418830a92} 28 | 29 | 30 | 31 | 32 | $(ProjectDir)..\..\..;$(ProjectDir)..\..\..\test;$(ProjectDir)..\..\..\..\foundation;$(ProjectDir)..\..\..\..\foundation_lib;$(ProjectDir)..\..\..\..\foundation\test;$(ProjectDir)..\..\..\..\foundation_lib\test;%(AdditionalIncludeDirectories) 33 | 34 | 35 | Console 36 | test.lib;foundation.lib;%(AdditionalDependencies) 37 | 38 | 39 | 40 | 41 | $(ProjectDir)..\..\..\..\foundation_lib\lib\windows\debug\x86-64 42 | false 43 | 44 | 45 | 46 | 47 | $(ProjectDir)..\..\..\..\foundation_lib\lib\windows\release\x86-64 48 | false 49 | 50 | 51 | 52 | 53 | $(ProjectDir)..\..\..\..\foundation_lib\lib\windows\profile\x86-64 54 | 55 | 56 | 57 | 58 | $(ProjectDir)..\..\..\..\foundation_lib\lib\windows\deploy\x86-64 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /build/ninja/android.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """Ninja toolchain abstraction for Android platform""" 4 | 5 | import os 6 | import subprocess 7 | 8 | import toolchain 9 | 10 | def make_target(toolchain, host, target): 11 | return Android(toolchain, host, target) 12 | 13 | class Android(object): 14 | def __init__(self, toolchain, host, target): 15 | self.host = host 16 | 17 | if host.is_windows(): 18 | self.exe_suffix = '.exe' 19 | else: 20 | self.exe_suffix = '' 21 | 22 | self.javaccmd = toolchain.mkdircmd('$outpath') + ' && $javac -d $outpath -classpath $outpath -sourcepath $sourcepath -target 1.5 -bootclasspath $androidjar -g -source 1.5 -Xlint:-options $in' 23 | self.dexcmd = '$dex --dex --output $out $in' 24 | self.aaptcmd = toolchain.cdcmd('$apkbuildpath') + ' && $aapt p -f -m -M AndroidManifest.xml -F $apk -I $androidjar -S res --debug-mode --no-crunch -J gen $aaptflags' 25 | self.aaptdeploycmd = toolchain.cdcmd('$apkbuildpath') + ' && $aapt c -S res -C bin/res && $aapt p -f -m -M AndroidManifest.xml -F $apk -I $androidjar -S bin/res -S res -J gen $aaptflags' 26 | self.aaptaddcmd = toolchain.cdcmd('$apkbuildpath') + ' && ' + toolchain.copycmd('$apksource', '$apk' ) + ' && $aapt a $apk $apkaddfiles' 27 | self.zipcmd = '$zip -r -9 $out $in $implicitin' 28 | self.zipaligncmd = '$zipalign -f 4 $in $out' 29 | self.codesigncmd = 'build/ninja/codesign.py --target $target --prefs codesign.json --zipfile $in --config $config --jarsigner $jarsigner $out' 30 | 31 | if host.is_windows(): 32 | self.codesigncmd = 'python ' + self.codesigncmd 33 | 34 | def initialize_toolchain(self): 35 | self.ndkpath = os.getenv('NDK_HOME', '') 36 | self.sdkpath = os.getenv('ANDROID_HOME', '') 37 | self.sysroot = '' 38 | self.platformversion = '21' 39 | self.gcc_toolchainversion = '4.9' 40 | self.javasdk = '' 41 | 42 | self.archname = dict() 43 | self.archname['x86'] = 'x86' 44 | self.archname['x86-64'] = 'x86_64' 45 | self.archname['arm6'] = 'arm' 46 | self.archname['arm7'] = 'arm' 47 | self.archname['arm64'] = 'arm64' 48 | self.archname['mips'] = 'mips' 49 | self.archname['mips64'] = 'mips64' 50 | 51 | self.archpath = dict() 52 | self.archpath['x86'] = 'x86' 53 | self.archpath['x86-64'] = 'x86-64' 54 | self.archpath['arm6'] = 'armeabi' 55 | self.archpath['arm7'] = 'armeabi-v7a' 56 | self.archpath['arm64'] = 'arm64-v8a' 57 | self.archpath['mips'] = 'mips' 58 | self.archpath['mips64'] = 'mips64' 59 | 60 | self.gcc_toolchainname = dict() 61 | self.gcc_toolchainname['x86'] = 'x86-' + self.gcc_toolchainversion 62 | self.gcc_toolchainname['x86-64'] = 'x86_64-' + self.gcc_toolchainversion 63 | self.gcc_toolchainname['arm6'] = 'arm-linux-androideabi-' + self.gcc_toolchainversion 64 | self.gcc_toolchainname['arm7'] = 'arm-linux-androideabi-' + self.gcc_toolchainversion 65 | self.gcc_toolchainname['arm64'] = 'aarch64-linux-android-' + self.gcc_toolchainversion 66 | self.gcc_toolchainname['mips'] = 'mipsel-linux-android-' + self.gcc_toolchainversion 67 | self.gcc_toolchainname['mips64'] = 'mips64el-linux-android-' + self.gcc_toolchainversion 68 | 69 | self.gcc_toolchainprefix = dict() 70 | self.gcc_toolchainprefix['x86'] = 'i686-linux-android-' 71 | self.gcc_toolchainprefix['x86-64'] = 'x86_64-linux-android-' 72 | self.gcc_toolchainprefix['arm6'] = 'arm-linux-androideabi-' 73 | self.gcc_toolchainprefix['arm7'] = 'arm-linux-androideabi-' 74 | self.gcc_toolchainprefix['arm64'] = 'aarch64-linux-android-' 75 | self.gcc_toolchainprefix['mips'] = 'mipsel-linux-android-' 76 | self.gcc_toolchainprefix['mips64'] = 'mips64el-linux-android-' 77 | 78 | if self.host.is_windows(): 79 | if os.getenv('PROCESSOR_ARCHITECTURE', 'AMD64').find('64') != -1: 80 | self.hostarchname = 'windows-x86_64' 81 | else: 82 | self.hostarchname = 'windows-x86' 83 | elif self.host.is_linux(): 84 | localarch = toolchain.check_output(['uname', '-m']) 85 | if localarch == 'x86_64': 86 | self.hostarchname = 'linux-x86_64' 87 | else: 88 | self.hostarchname = 'linux-x86' 89 | elif self.host.is_macos(): 90 | self.hostarchname = 'darwin-x86_64' 91 | 92 | def build_toolchain(self): 93 | buildtools_path = os.path.join(self.sdkpath, 'build-tools') 94 | buildtools_list = [item for item in os.listdir(buildtools_path) if os.path.isdir(os.path.join(buildtools_path, item))] 95 | buildtools_list.sort(key = lambda s: map(int, s.split('-')[0].split('.'))) 96 | 97 | self.buildtools_path = os.path.join(self.sdkpath, 'build-tools', buildtools_list[-1]) 98 | self.android_jar = os.path.join(self.sdkpath, 'platforms', 'android-' + self.platformversion, 'android.jar') 99 | 100 | self.javac = 'javac' 101 | self.jarsigner = 'jarsigner' 102 | if self.javasdk != '': 103 | self.javac = os.path.join(self.javasdk, 'bin', self.javac) 104 | self.jarsigner = os.path.join(self.javasdk, 'bin', self.jarsigner) 105 | if self.host.is_windows(): 106 | self.dex = os.path.join(self.buildtools_path, 'dx.bat') 107 | else: 108 | self.dex = os.path.join(self.buildtools_path, 'dx' + self.exe_suffix) 109 | if not os.path.isfile(self.dex): 110 | self.dex = os.path.join(self.sdkpath, 'tools', 'dx' + self.exe_suffix) 111 | self.aapt = os.path.join(self.buildtools_path, 'aapt' + self.exe_suffix) 112 | self.zipalign = os.path.join(self.buildtools_path, 'zipalign' + self.exe_suffix) 113 | if not os.path.isfile( self.zipalign ): 114 | self.zipalign = os.path.join(self.sdkpath, 'tools', 'zipalign' + self.exe_suffix) 115 | 116 | def parse_prefs(self, prefs): 117 | if 'android' in prefs: 118 | androidprefs = prefs['android'] 119 | if 'ndkpath' in androidprefs: 120 | self.ndkpath = os.path.expanduser(androidprefs['ndkpath']) 121 | if 'sdkpath' in androidprefs: 122 | self.sdkpath = os.path.expanduser(androidprefs['sdkpath']) 123 | if 'platformversion' in androidprefs: 124 | self.platformversion = androidprefs['platformversion'] 125 | if 'gccversion' in androidprefs: 126 | self.gcc_toolchainversion = androidprefs['gccversion'] 127 | if 'javasdk' in androidprefs: 128 | self.javasdk = androidprefs['javasdk'] 129 | 130 | def write_variables(self, writer): 131 | writer.variable('ndk', self.ndkpath) 132 | writer.variable('sdk', self.sdkpath) 133 | writer.variable('sysroot', self.sysroot) 134 | writer.variable('androidjar', self.android_jar ) 135 | writer.variable('apkbuildpath', '') 136 | writer.variable('apk', '') 137 | writer.variable('apksource', '') 138 | writer.variable('apkaddfiles', '') 139 | writer.variable('javac', self.javac) 140 | writer.variable('dex', self.dex) 141 | writer.variable('aapt', self.aapt) 142 | writer.variable('zipalign', self.zipalign) 143 | writer.variable('jarsigner', self.jarsigner) 144 | writer.variable('aaptflags', '') 145 | 146 | def write_rules(self, writer): 147 | writer.rule('aapt', command = self.aaptcmd, description = 'AAPT $out') 148 | writer.rule('aaptdeploy', command = self.aaptdeploycmd, description = 'AAPT $out') 149 | writer.rule('aaptadd', command = self.aaptaddcmd, description = 'AAPT $out') 150 | writer.rule('javac', command = self.javaccmd, description = 'JAVAC $in') 151 | writer.rule('dex', command = self.dexcmd, description = 'DEX $out') 152 | writer.rule('zip', command = self.zipcmd, description = 'ZIP $out') 153 | writer.rule('zipalign', command = self.zipaligncmd, description = 'ZIPALIGN $out') 154 | writer.rule('codesign', command = self.codesigncmd, description = 'CODESIGN $out') 155 | 156 | def make_sysroot_path(self, arch): 157 | return os.path.join(self.ndkpath, 'platforms', 'android-' + self.platformversion, 'arch-' + self.archname[arch]) 158 | 159 | def make_gcc_toolchain_path(self, arch): 160 | return os.path.join(self.ndkpath, 'toolchains', self.gcc_toolchainname[arch], 'prebuilt', self.hostarchname) 161 | 162 | def make_gcc_bin_path(self, arch): 163 | return os.path.join(self.make_gcc_toolchain_path(arch), 'bin', self.gcc_toolchainprefix[arch]) 164 | 165 | def archname(self): 166 | return self.archname 167 | 168 | def archpath(self): 169 | return self.archpath 170 | 171 | def hostarchname(self): 172 | return self.hostarchname 173 | 174 | def apk(self, toolchain, writer, module, archbins, javasources, outpath, binname, basepath, config, implicit_deps, resources): 175 | buildpath = os.path.join('$buildpath', config, 'apk', binname) 176 | baseapkname = binname + ".base.apk" 177 | unsignedapkname = binname + ".unsigned.apk" 178 | unalignedapkname = binname + ".unaligned.apk" 179 | apkname = binname + ".apk" 180 | apkfiles = [] 181 | libfiles = [] 182 | locallibs = [] 183 | resfiles = [] 184 | manifestfile = [] 185 | 186 | writer.comment('Make APK') 187 | for _, value in archbins.iteritems(): 188 | for archbin in value: 189 | archpair = os.path.split(archbin) 190 | libname = archpair[1] 191 | arch = os.path.split(archpair[0])[1] 192 | locallibpath = os.path.join('lib', self.archpath[arch], libname) 193 | archpath = os.path.join(buildpath, locallibpath) 194 | locallibs += [locallibpath + ' '] 195 | libfiles += toolchain.copy(writer, archbin, archpath) 196 | for resource in resources: 197 | filename = os.path.split(resource)[1] 198 | if filename == 'AndroidManifest.xml': 199 | manifestfile = toolchain.copy(writer, os.path.join(basepath, module, resource), os.path.join(buildpath, 'AndroidManifest.xml')) 200 | else: 201 | restype = os.path.split(os.path.split(resource)[0])[1] 202 | if restype == 'asset': 203 | pass #todo: implement 204 | else: 205 | resfiles += toolchain.copy(writer, os.path.join(basepath, module, resource), os.path.join(buildpath, 'res', restype, filename)) 206 | 207 | #Make directories 208 | gendir = toolchain.mkdir(writer, os.path.join(buildpath, 'gen')) 209 | bindir = toolchain.mkdir(writer, os.path.join(buildpath, 'bin')) 210 | binresdir = toolchain.mkdir(writer, os.path.join(buildpath, 'bin', 'res'), order_only = bindir) 211 | alldirs = gendir + bindir + binresdir 212 | 213 | aaptvars = [('apkbuildpath', buildpath), ('apk', baseapkname)] 214 | aaptout = os.path.join(buildpath, baseapkname) 215 | if config == 'deploy': 216 | baseapkfile = writer.build(aaptout, 'aaptdeploy', manifestfile, variables = aaptvars, implicit = manifestfile + resfiles, order_only = alldirs) 217 | else: 218 | baseapkfile = writer.build(aaptout, 'aapt', manifestfile, variables = aaptvars, implicit = manifestfile + resfiles, order_only = alldirs) 219 | 220 | #Compile java code 221 | javafiles = [] 222 | localjava = [] 223 | if javasources != []: 224 | #self.javaccmd = '$javac -d $outpath -classpath $outpath -sourcepath $sourcepath -target 1.5 -bootclasspath $androidjar -g -source 1.5 -Xlint:-options $in' 225 | #self.dexcmd = '$dex --dex --output $out $in' 226 | javasourcepath = '.' 227 | if self.host.is_windows(): 228 | javasourcepath += ';' 229 | else: 230 | javasourcepath += ':' 231 | javasourcepath += os.path.join(buildpath, 'gen') 232 | classpath = os.path.join(buildpath, 'classes') 233 | javavars = [('outpath', classpath), ('sourcepath', javasourcepath)] 234 | javaclasses = writer.build(classpath, 'javac', javasources, variables = javavars, implicit = baseapkfile) 235 | localjava += ['classes.dex'] 236 | javafiles += writer.build(os.path.join(buildpath, 'classes.dex'), 'dex', classpath) 237 | 238 | #Add native libraries and java classes to apk 239 | aaptvars = [('apkbuildpath', buildpath), ('apk', unsignedapkname), ('apksource', baseapkname), ('apkaddfiles', toolchain.paths_forward_slash(locallibs + localjava))] 240 | unsignedapkfile = writer.build(os.path.join(buildpath, unsignedapkname), 'aaptadd', baseapkfile, variables = aaptvars, implicit = libfiles + javafiles, order_only = alldirs) 241 | 242 | #Sign the APK 243 | codesignvars = [('config', config)] 244 | unalignedapkfile = writer.build(os.path.join(buildpath, unalignedapkname), 'codesign', unsignedapkfile, variables = codesignvars) 245 | 246 | #Run zipalign 247 | outfile = writer.build(os.path.join(outpath, config, apkname), 'zipalign', unalignedapkfile) 248 | return outfile 249 | -------------------------------------------------------------------------------- /build/ninja/codesign.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """Codesign utility""" 4 | 5 | import argparse 6 | import subprocess 7 | import os 8 | import time 9 | import shutil 10 | import json 11 | 12 | parser = argparse.ArgumentParser(description = 'Codesign utility for Ninja builds') 13 | parser.add_argument('file', type=str, 14 | help = 'Bundle/package to sign') 15 | parser.add_argument('--target', type=str, 16 | help = 'Target', 17 | choices = ['macos', 'ios', 'android'], 18 | default = '') 19 | parser.add_argument('--bundle', type=str, 20 | help = 'Bundle identifier (OSX/iOS)', 21 | default = '') 22 | parser.add_argument('--organisation', type=str, 23 | help = 'Organisation identifier (OSX/iOS)', 24 | default = '') 25 | parser.add_argument('--provisioning', type=str, 26 | help = 'Provisioning profile (OSX/iOS)', 27 | default = '') 28 | parser.add_argument('--builddir', type=str, 29 | help = 'Build directory (OSX/iOS)', 30 | default = '') 31 | parser.add_argument('--binname', type=str, 32 | help = 'Binary name (OSX/iOS)', 33 | default = '') 34 | parser.add_argument('--zipfile', type=str, 35 | help = 'Zip file (Android)', 36 | default = '') 37 | parser.add_argument('--tsacert', type=str, 38 | help = 'TSA cert (Android)', 39 | default = '') 40 | parser.add_argument('--tsa', type=str, 41 | help = 'TSA (Android)', 42 | default = '') 43 | parser.add_argument('--keystore', type=str, 44 | help = 'Keystore (Android)', 45 | default = '') 46 | parser.add_argument('--keystorepass', type=str, 47 | help = 'Keystore password (Android)', 48 | default = '') 49 | parser.add_argument('--keyalias', type=str, 50 | help = 'Key alias (Android)', 51 | default = '') 52 | parser.add_argument('--keypass', type=str, 53 | help = 'Key password (Android)', 54 | default = '') 55 | parser.add_argument('--jarsigner', type=str, 56 | help = 'JAR signer (Android)', 57 | default = 'jarsigner') 58 | parser.add_argument('--prefs', type=str, 59 | help = 'Preferences file', 60 | default = '') 61 | parser.add_argument('--config', type=str, 62 | help = 'Build configuration', 63 | default = '') 64 | parser.add_argument('--entitlements', type=str, 65 | help = 'Entitlements file', 66 | default = '') 67 | options = parser.parse_args() 68 | 69 | androidprefs = {} 70 | iosprefs = {} 71 | macosprefs = {} 72 | 73 | 74 | def parse_prefs( prefsfile ): 75 | global androidprefs 76 | global iosprefs 77 | global macosprefs 78 | if not os.path.isfile( prefsfile ): 79 | return 80 | file = open( prefsfile, 'r' ) 81 | prefs = json.load( file ) 82 | file.close() 83 | if 'android' in prefs: 84 | androidprefs = prefs['android'] 85 | if 'ios' in prefs: 86 | iosprefs = prefs['ios'] 87 | if 'macos' in prefs: 88 | macosprefs = prefs['macos'] 89 | 90 | 91 | def codesign_ios(): 92 | if not 'organisation' in iosprefs: 93 | iosprefs['organisation'] = options.organisation 94 | if not 'bundleidentifier' in iosprefs: 95 | iosprefs['bundleidentifier'] = options.bundle 96 | if not 'provisioning' in iosprefs: 97 | iosprefs['provisioning'] = options.provisioning 98 | 99 | sdkdir = subprocess.check_output( [ 'xcrun', '--sdk', 'iphoneos', '--show-sdk-path' ] ).decode().strip().splitlines()[-1] 100 | entitlements = os.path.join( sdkdir, 'Entitlements.plist' ) 101 | plistpath = os.path.join( options.builddir, 'Entitlements.xcent' ) 102 | 103 | platformpath = subprocess.check_output( [ 'xcrun', '--sdk', 'iphoneos', '--show-sdk-platform-path' ] ).decode().strip().splitlines()[-1] 104 | localpath = platformpath + "/Developer/usr/bin:/Applications/Xcode.app/Contents/Developer/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin" 105 | plutil = "PATH=" + localpath + " " + subprocess.check_output( [ 'xcrun', '--sdk', 'iphoneos', '-f', 'plutil' ] ).decode().strip().splitlines()[-1] 106 | 107 | shutil.copyfile( entitlements, plistpath ) 108 | os.system( plutil + ' -convert xml1 ' + plistpath ) 109 | 110 | f = open( plistpath, 'r' ) 111 | lines = [ line.strip( '\n\r' ) for line in f ] 112 | f.close() 113 | 114 | for i in range( 0, len( lines ) ): 115 | if lines[i].find( '$(AppIdentifierPrefix)' ) != -1: 116 | lines[i] = lines[i].replace( '$(AppIdentifierPrefix)', iosprefs['organisation'] + '.' ) 117 | if lines[i].find( '$(CFBundleIdentifier)' ) != -1: 118 | lines[i] = lines[i].replace( '$(CFBundleIdentifier)', iosprefs['bundleidentifier'] ) 119 | if lines[i].find( '$(binname)' ) != -1: 120 | lines[i] = lines[i].replace( '$(binname)', options.binname ) 121 | 122 | with open( plistpath, 'w' ) as plist_file: 123 | for line in lines: 124 | if options.config != 'deploy' and line == '': 125 | plist_file.write( '\tget-task-allow\n' ) 126 | plist_file.write( '\t\n' ) 127 | plist_file.write( line + '\n' ) 128 | plist_file.close() 129 | 130 | if os.path.isfile( os.path.join( options.file, '_CodeSignature', 'CodeResources' ) ): 131 | os.remove( os.path.join( options.file, '_CodeSignature', 'CodeResources' ) ) 132 | 133 | os.system( '/usr/bin/codesign --force --sign "' + iosprefs['signature'] + '" --entitlements ' + plistpath + ' ' + options.file ) 134 | 135 | if os.path.isfile( os.path.join( options.file, '_CodeSignature', 'CodeResources' ) ): 136 | os.utime( os.path.join( options.file, '_CodeSignature', 'CodeResources' ), None ) 137 | os.utime( os.path.join( options.file, '_CodeSignature' ), None ) 138 | os.utime( options.file, None ) 139 | 140 | 141 | def codesign_macos(): 142 | if not 'organisation' in macosprefs: 143 | macosprefs['organisation'] = options.organisation 144 | if not 'bundleidentifier' in macosprefs: 145 | macosprefs['bundleidentifier'] = options.bundle 146 | if not 'provisioning' in macosprefs: 147 | macosprefs['provisioning'] = options.provisioning 148 | if not 'entitlements' in macosprefs: 149 | macosprefs['entitlements'] = options.entitlements 150 | 151 | codesign_allocate = subprocess.check_output( [ 'xcrun', '--sdk', 'macosx', '-f', 'codesign_allocate' ] ).decode().strip().splitlines()[-1] 152 | sdkdir = subprocess.check_output( [ 'xcrun', '--sdk', 'macosx', '--show-sdk-path' ] ).decode().strip().splitlines()[-1] 153 | entitlements = os.path.join( sdkdir, 'Entitlements.plist' ) 154 | 155 | if os.path.isfile( os.path.join( options.file, 'Contents', '_CodeSignature', 'CodeResources' ) ): 156 | os.remove( os.path.join( options.file, 'Contents', '_CodeSignature', 'CodeResources' ) ) 157 | 158 | if 'signature' in macosprefs: 159 | command = 'export CODESIGN_ALLOCATE=' + codesign_allocate + '; /usr/bin/codesign --force --sign "' + macosprefs['signature'] + '" -o runtime ' 160 | if ('entitlements' in macosprefs) and (macosprefs['entitlements'] != '') and (macosprefs['entitlements'] != 'none'): 161 | command = command + '--entitlements ' + macosprefs['entitlements'] + ' --generate-entitlement-der ' 162 | command = command + options.file 163 | # print(command) 164 | os.system(command) 165 | 166 | if os.path.isfile( os.path.join( options.file, 'Contents', '_CodeSignature', 'CodeResources' ) ): 167 | os.utime( os.path.join( options.file, 'Contents', '_CodeSignature', 'CodeResources' ), None ) 168 | os.utime( os.path.join( options.file, 'Contents', '_CodeSignature' ), None ) 169 | os.utime( os.path.join( options.file, 'Contents' ), None ) 170 | os.utime( options.file, None ) 171 | 172 | 173 | def codesign_android(): 174 | if not 'tsacert' in androidprefs: 175 | androidprefs['tsacert'] = options.tsacert 176 | if not 'tsa' in androidprefs: 177 | androidprefs['tsa'] = options.tsa 178 | if not 'keystore' in androidprefs: 179 | androidprefs['keystore'] = options.keystore 180 | if not 'keystorepass' in androidprefs: 181 | androidprefs['keystorepass'] = options.keystorepass 182 | if not 'keyalias' in androidprefs: 183 | androidprefs['keyalias'] = options.keyalias 184 | if not 'keypass' in androidprefs: 185 | androidprefs['keypass'] = options.keypass 186 | if not 'jarsigner' in androidprefs: 187 | androidprefs['jarsigner'] = options.jarsigner 188 | 189 | timestamp = '' 190 | if androidprefs['tsacert'] != '': 191 | timestamp = '-tsacert ' + androidprefs['tsacert'] 192 | elif androidprefs['tsa'] != '': 193 | timestamp = '-tsa ' + androidprefs['tsa'] 194 | 195 | proxy = '' 196 | if 'proxy' in androidprefs and androidprefs['proxy'] != '' and androidprefs['proxy'] != 'None': 197 | proxy = androidprefs['proxy'] 198 | if proxy != '' and proxy != 'None': 199 | defstr = "-J-Dhttp.proxy" 200 | url = urlparse.urlparse(proxy) 201 | if url.scheme == 'https': 202 | defstr = "-J-Dhttps.proxy" 203 | host = url.netloc 204 | port = '' 205 | username = '' 206 | password = '' 207 | if '@' in host: 208 | username, host = host.split(':', 1) 209 | password, host = host.split('@', 1) 210 | if ':' in host: 211 | host, port = host.split(':', 1) 212 | proxy = defstr + "Host=" + host 213 | if port != '': 214 | proxy += " " + defstr + "Port=" + port 215 | if username != '': 216 | proxy += " " + defstr + "User=" + username 217 | if password != '': 218 | proxy += " " + defstr + "Password=" + password 219 | 220 | signcmd = androidprefs['jarsigner'] + ' ' + timestamp + ' -sigalg SHA1withRSA -digestalg SHA1 -keystore ' + androidprefs['keystore'] + ' -storepass ' + androidprefs['keystorepass'] + ' -keypass ' + androidprefs['keypass'] + ' -signedjar ' + options.file + ' ' + options.zipfile + ' ' + androidprefs['keyalias'] + ' ' + proxy 221 | os.system(signcmd) 222 | 223 | 224 | parse_prefs( options.prefs ) 225 | 226 | if options.target == 'ios': 227 | codesign_ios() 228 | elif options.target == 'macos': 229 | codesign_macos() 230 | elif options.target == 'android': 231 | codesign_android() 232 | -------------------------------------------------------------------------------- /build/ninja/gcc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """Ninja toolchain abstraction for GCC compiler suite""" 4 | 5 | import os 6 | 7 | import toolchain 8 | 9 | class GCCToolchain(toolchain.Toolchain): 10 | 11 | def initialize(self, project, archs, configs, includepaths, dependlibs, libpaths, variables, subninja): 12 | #Local variable defaults 13 | self.toolchain = '' 14 | self.includepaths = [] 15 | self.libpaths = libpaths 16 | self.ccompiler = os.environ.get('CC') or 'gcc' 17 | self.cxxcompiler = os.environ.get('CXX') or 'g++' 18 | self.archiver = os.environ.get('AR') or 'ar' 19 | self.linker = os.environ.get('CC') or 'gcc' 20 | self.cxxlinker = os.environ.get('CXX') or 'g++' 21 | 22 | #Command definitions 23 | self.cccmd = '$toolchain$cc -MMD -MT $out -MF $out.d $includepaths $moreincludepaths $cflags $carchflags $cconfigflags $cmoreflags $cenvflags -c $in -o $out' 24 | self.cxxcmd = '$toolchain$cxx -MMD -MT $out -MF $out.d $includepaths $moreincludepaths $cxxflags $carchflags $cconfigflags $cmoreflags $cxxenvflags -c $in -o $out' 25 | self.ccdeps = 'gcc' 26 | self.ccdepfile = '$out.d' 27 | self.arcmd = self.rmcmd('$out') + ' && $toolchain$ar crsD $ararchflags $arflags $arenvflags $out $in' 28 | self.linkcmd = '$toolchain$link $libpaths $configlibpaths $linkflags $linkarchflags $linkconfigflags $linkenvflags -o $out $in $libs $archlibs $oslibs' 29 | 30 | #Base flags 31 | self.cflags = ['-D' + project.upper() + '_COMPILE=1', 32 | '-funit-at-a-time', '-fstrict-aliasing', 33 | '-fno-math-errno','-ffinite-math-only', '-funsafe-math-optimizations', 34 | '-fno-trapping-math', '-ffast-math'] 35 | self.cwarnflags = ['-Wextra', '-Wall', '-Werror'] 36 | self.cmoreflags = [] 37 | self.mflags = [] 38 | self.arflags = [] 39 | self.linkflags = [] 40 | self.oslibs = [] 41 | 42 | self.initialize_subninja(subninja) 43 | self.initialize_archs(archs) 44 | self.initialize_configs(configs) 45 | self.initialize_project(project) 46 | self.initialize_toolchain() 47 | self.initialize_depends(dependlibs) 48 | 49 | self.parse_default_variables(variables) 50 | self.read_build_prefs() 51 | 52 | if self.target.is_linux() or self.target.is_bsd() or self.target.is_raspberrypi() or self.target.is_sunos(): 53 | self.cflags += ['-D_GNU_SOURCE=1'] 54 | self.linkflags += ['-pthread'] 55 | if self.target.is_linux() or self.target.is_raspberrypi(): 56 | self.oslibs += ['dl'] 57 | if self.target.is_raspberrypi(): 58 | self.linkflags += ['-latomic'] 59 | if self.target.is_bsd(): 60 | self.oslibs += ['execinfo'] 61 | if self.target.is_haiku(): 62 | self.cflags += ['-D_GNU_SOURCE=1'] 63 | self.linkflags += ['-lpthread'] 64 | 65 | self.includepaths = self.prefix_includepaths((includepaths or []) + ['.']) 66 | 67 | if self.is_monolithic(): 68 | self.cflags += ['-DBUILD_MONOLITHIC=1'] 69 | if self.use_coverage(): 70 | self.cflags += ['--coverage'] 71 | self.linkflags += ['--coverage'] 72 | 73 | if not 'nowarning' in variables or not variables['nowarning']: 74 | self.cflags += self.cwarnflags 75 | self.cxxflags = list(self.cflags) 76 | 77 | self.cflags += ['-std=c11'] 78 | if self.target.is_macos() or self.target.is_ios(): 79 | self.cxxflags += ['-std=c++14', '-stdlib=libc++'] 80 | else: 81 | self.cxxflags += ['-std=gnu++14'] 82 | 83 | #Overrides 84 | self.objext = '.o' 85 | 86 | #Builders 87 | self.builders['c'] = self.builder_cc 88 | self.builders['cc'] = self.builder_cxx 89 | self.builders['cpp'] = self.builder_cxx 90 | self.builders['lib'] = self.builder_lib 91 | self.builders['multilib'] = self.builder_multicopy 92 | self.builders['sharedlib'] = self.builder_sharedlib 93 | self.builders['multisharedlib'] = self.builder_multicopy 94 | self.builders['bin'] = self.builder_bin 95 | self.builders['multibin'] = self.builder_multicopy 96 | 97 | #Setup target platform 98 | self.build_target_toolchain(self.target) 99 | 100 | def name(self): 101 | return 'gcc' 102 | 103 | def parse_prefs(self, prefs): 104 | super(GCCToolchain, self).parse_prefs(prefs) 105 | if 'gcc' in prefs: 106 | gccprefs = prefs['gcc'] 107 | if 'toolchain' in gccprefs: 108 | self.toolchain = gccprefs['toolchain'] 109 | if os.path.split(self.toolchain)[1] != 'bin': 110 | self.toolchain = os.path.join(self.toolchain, 'bin') 111 | 112 | def write_variables(self, writer): 113 | super(GCCToolchain, self).write_variables(writer) 114 | writer.variable('toolchain', self.toolchain) 115 | writer.variable('cc', self.ccompiler) 116 | writer.variable('cxx', self.cxxcompiler) 117 | writer.variable('ar', self.archiver) 118 | writer.variable('link', self.linker) 119 | writer.variable('includepaths', self.make_includepaths(self.includepaths)) 120 | writer.variable('moreincludepaths', '') 121 | writer.variable('cflags', self.cflags) 122 | writer.variable('cxxflags', self.cxxflags) 123 | writer.variable('carchflags', '') 124 | writer.variable('cconfigflags', '') 125 | writer.variable('cmoreflags', self.cmoreflags) 126 | writer.variable('cenvflags', (os.environ.get('CFLAGS') or '').split()) 127 | writer.variable('cxxenvflags', (os.environ.get('CXXFLAGS') or '').split()) 128 | writer.variable('arflags', self.arflags) 129 | writer.variable('ararchflags', '') 130 | writer.variable('arconfigflags', '') 131 | writer.variable('arenvflags', (os.environ.get('ARFLAGS') or '').split()) 132 | writer.variable('linkflags', self.linkflags) 133 | writer.variable('linkarchflags', '') 134 | writer.variable('linkconfigflags', '') 135 | writer.variable('linkenvflags', (os.environ.get('LDFLAGS') or '').split()) 136 | writer.variable('libs', '') 137 | writer.variable('libpaths', self.make_libpaths(self.libpaths)) 138 | writer.variable('configlibpaths', '') 139 | writer.variable('archlibs', '') 140 | writer.variable('oslibs', self.make_libs(self.oslibs)) 141 | writer.newline() 142 | 143 | def write_rules(self, writer): 144 | super(GCCToolchain, self).write_rules(writer) 145 | writer.rule('cc', command = self.cccmd, depfile = self.ccdepfile, deps = self.ccdeps, description = 'CC $in') 146 | writer.rule('cxx', command = self.cxxcmd, depfile = self.ccdepfile, deps = self.ccdeps, description = 'CXX $in') 147 | writer.rule('ar', command = self.arcmd, description = 'LIB $out') 148 | writer.rule('link', command = self.linkcmd, description = 'LINK $out') 149 | writer.rule('so', command = self.linkcmd, description = 'SO $out') 150 | writer.newline() 151 | 152 | def build_target_toolchain(self, target): 153 | if target.is_windows(): 154 | self.build_windows_toolchain() 155 | if self.toolchain != '' and not self.toolchain.endswith('/') and not self.toolchain.endswith('\\'): 156 | self.toolchain += os.sep 157 | 158 | def build_windows_toolchain(self): 159 | self.cflags += ['-U__STRICT_ANSI__'] 160 | self.oslibs = ['kernel32', 'user32', 'shell32', 'advapi32'] 161 | 162 | def make_includepath(self, path): 163 | if os.path.isabs(path) or self.subninja == '': 164 | return self.path_escape(path) 165 | if path == '.': 166 | return self.path_escape(self.subninja) 167 | return self.path_escape(os.path.join(self.subninja, path)) 168 | 169 | def make_includepaths(self, includepaths): 170 | if not includepaths is None: 171 | return ['-I' + self.make_includepath(path) for path in list(includepaths)] 172 | return [] 173 | 174 | def make_libpath(self, path): 175 | return self.path_escape(path) 176 | 177 | def make_libpaths(self, libpaths): 178 | if not libpaths is None: 179 | return ['-L' + self.make_libpath(path) for path in libpaths] 180 | return [] 181 | 182 | def make_targetarchflags(self, arch, targettype): 183 | flags = [] 184 | if arch == 'x86': 185 | flags += ['-m32'] 186 | elif arch == 'x86-64': 187 | flags += ['-m64'] 188 | return flags 189 | 190 | def make_carchflags(self, arch, targettype): 191 | flags = [] 192 | if targettype == 'sharedlib': 193 | flags += ['-DBUILD_DYNAMIC_LINK=1'] 194 | if self.target.is_linux() or self.target.is_bsd() or self.target.is_sunos(): 195 | flags += ['-fPIC'] 196 | flags += self.make_targetarchflags(arch, targettype) 197 | return flags 198 | 199 | def make_cconfigflags(self, config, targettype): 200 | flags = [] 201 | if config == 'debug': 202 | flags += ['-DBUILD_DEBUG=1', '-g'] 203 | elif config == 'release': 204 | flags += ['-DBUILD_RELEASE=1', '-O3', '-g', '-funroll-loops'] 205 | elif config == 'profile': 206 | flags += ['-DBUILD_PROFILE=1', '-O3', '-g', '-funroll-loops'] 207 | elif config == 'deploy': 208 | flags += ['-DBUILD_DEPLOY=1', '-O3', '-g', '-funroll-loops'] 209 | return flags 210 | 211 | def make_ararchflags(self, arch, targettype): 212 | flags = [] 213 | return flags 214 | 215 | def make_arconfigflags(self, config, targettype): 216 | flags = [] 217 | return flags 218 | 219 | def make_linkarchflags(self, arch, targettype): 220 | flags = [] 221 | flags += self.make_targetarchflags(arch, targettype) 222 | return flags 223 | 224 | def make_linkconfigflags(self, config, targettype): 225 | flags = [] 226 | if self.target.is_windows(): 227 | if targettype == 'sharedlib': 228 | flags += ['-Xlinker', '/DLL'] 229 | elif targettype == 'bin': 230 | flags += ['-Xlinker', '/SUBSYSTEM:CONSOLE'] 231 | elif self.target.is_macos() or self.target.is_ios(): 232 | if targettype == 'sharedlib' or targettype == 'multisharedlib': 233 | flags += ['-dynamiclib'] 234 | else: 235 | if targettype == 'sharedlib': 236 | flags += ['-shared'] 237 | return flags 238 | 239 | def make_libs(self, libs): 240 | if libs != None: 241 | return ['-l' + lib for lib in libs] 242 | return [] 243 | 244 | def make_configlibpaths(self, config, arch, extralibpaths): 245 | libpaths = [ 246 | self.libpath, 247 | os.path.join(self.libpath, arch), 248 | os.path.join(self.libpath, config), 249 | os.path.join(self.libpath, config, arch) 250 | ] 251 | if extralibpaths != None: 252 | libpaths += [os.path.join(libpath, self.libpath) for libpath in extralibpaths] 253 | libpaths += [os.path.join(libpath, self.libpath, arch) for libpath in extralibpaths] 254 | libpaths += [os.path.join(libpath, self.libpath, config) for libpath in extralibpaths] 255 | libpaths += [os.path.join(libpath, self.libpath, config, arch) for libpath in extralibpaths] 256 | return self.make_libpaths(libpaths) 257 | 258 | def cc_variables(self, config, arch, targettype, variables): 259 | localvariables = [] 260 | if 'includepaths' in variables: 261 | moreincludepaths = self.make_includepaths(variables['includepaths']) 262 | if not moreincludepaths == []: 263 | localvariables += [('moreincludepaths', moreincludepaths)] 264 | carchflags = self.make_carchflags(arch, targettype) 265 | if carchflags != []: 266 | localvariables += [('carchflags', carchflags)] 267 | cconfigflags = self.make_cconfigflags(config, targettype) 268 | if cconfigflags != []: 269 | localvariables += [('cconfigflags', cconfigflags)] 270 | if 'defines' in variables: 271 | localvariables += [('cmoreflags', ['-D' + define for define in variables['defines']])] 272 | return localvariables 273 | 274 | def ar_variables(self, config, arch, targettype, variables): 275 | localvariables = [] 276 | ararchflags = self.make_ararchflags(arch, targettype) 277 | if ararchflags != []: 278 | localvariables += [('ararchflags', ararchflags)] 279 | arconfigflags = self.make_arconfigflags(config, targettype) 280 | if arconfigflags != []: 281 | localvariables += [('arconfigflags', arconfigflags)] 282 | return localvariables 283 | 284 | def link_variables(self, config, arch, targettype, variables): 285 | localvariables = [] 286 | linkarchflags = self.make_linkarchflags(arch, targettype) 287 | if linkarchflags != []: 288 | localvariables += [('linkarchflags', linkarchflags)] 289 | linkconfigflags = self.make_linkconfigflags(config, targettype) 290 | if linkconfigflags != []: 291 | localvariables += [('linkconfigflags', linkconfigflags)] 292 | if 'libs' in variables: 293 | libvar = self.make_libs(variables['libs']) 294 | if libvar != []: 295 | localvariables += [('libs', libvar)] 296 | libpaths = [] 297 | if 'libpaths' in variables: 298 | libpaths = variables['libpaths'] 299 | localvariables += [('configlibpaths', self.make_configlibpaths(config, arch, libpaths))] 300 | 301 | if 'runtime' in variables and variables['runtime'] == 'c++': 302 | localvariables += [('link', self.cxxlinker)] 303 | 304 | return localvariables 305 | 306 | def builder_cc(self, writer, config, arch, targettype, infile, outfile, variables): 307 | return writer.build(outfile, 'cc', infile, implicit = self.implicit_deps(config, variables), variables = self.cc_variables(config, arch, targettype, variables)) 308 | 309 | def builder_cxx(self, writer, config, arch, targettype, infile, outfile, variables): 310 | return writer.build(outfile, 'cxx', infile, implicit = self.implicit_deps(config, variables), variables = self.cc_variables(config, arch, targettype, variables)) 311 | 312 | def builder_lib(self, writer, config, arch, targettype, infiles, outfile, variables): 313 | return writer.build(outfile, 'ar', infiles, implicit = self.implicit_deps(config, variables), variables = self.ar_variables(config, arch, targettype, variables)) 314 | 315 | def builder_sharedlib(self, writer, config, arch, targettype, infiles, outfile, variables): 316 | return writer.build(outfile, 'so', infiles, implicit = self.implicit_deps(config, variables), variables = self.link_variables(config, arch, targettype, variables)) 317 | 318 | def builder_bin(self, writer, config, arch, targettype, infiles, outfile, variables): 319 | return writer.build(outfile, 'link', infiles, implicit = self.implicit_deps(config, variables), variables = self.link_variables(config, arch, targettype, variables)) 320 | 321 | def create(host, target, toolchain): 322 | return GCCToolchain(host, target, toolchain) 323 | -------------------------------------------------------------------------------- /build/ninja/generator.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """Ninja build generator""" 4 | 5 | import argparse 6 | import os 7 | import pipes 8 | import sys 9 | 10 | import platform 11 | import toolchain 12 | import syntax 13 | 14 | class Generator(object): 15 | def __init__(self, project, includepaths = [], dependlibs = [], libpaths = [], variables = None): 16 | parser = argparse.ArgumentParser(description = 'Ninja build generator') 17 | parser.add_argument('-t', '--target', 18 | help = 'Target platform', 19 | choices = platform.supported_platforms()) 20 | parser.add_argument('--host', 21 | help = 'Host platform', 22 | choices = platform.supported_platforms()) 23 | parser.add_argument('--toolchain', 24 | help = 'Toolchain to use', 25 | choices = toolchain.supported_toolchains()) 26 | parser.add_argument('-c', '--config', action = 'append', 27 | help = 'Build configuration', 28 | choices = ['debug', 'release', 'profile', 'deploy'], 29 | default = []) 30 | parser.add_argument('-a', '--arch', action = 'append', 31 | help = 'Add architecture', 32 | choices = toolchain.supported_architectures(), 33 | default = []) 34 | parser.add_argument('-i', '--includepath', action = 'append', 35 | help = 'Add include path', 36 | default = []) 37 | parser.add_argument('--monolithic', action='store_true', 38 | help = 'Build monolithic test suite', 39 | default = False) 40 | parser.add_argument('--coverage', action='store_true', 41 | help = 'Build with code coverage', 42 | default = False) 43 | parser.add_argument('--subninja', action='store', 44 | help = 'Build as subproject (exclude rules and pools) with the given subpath', 45 | default = '') 46 | parser.add_argument('--notests', action='store_true', 47 | help = 'Skip building the internal tests', 48 | default = False) 49 | parser.add_argument('--buildprefs', action='store', 50 | help = 'Read the given build preferences file', 51 | default = '') 52 | parser.add_argument('--updatebuild', action='store_true', 53 | help = 'Update submodule build scripts', 54 | default = '') 55 | parser.add_argument('--lto', action='store_true', 56 | help = 'Build with Link Time Optimization', 57 | default = False) 58 | options = parser.parse_args() 59 | 60 | self.project = project 61 | self.target = platform.Platform(options.target) 62 | self.host = platform.Platform(options.host) 63 | self.subninja = options.subninja 64 | self.notests = options.notests 65 | archs = options.arch 66 | configs = options.config 67 | if includepaths is None: 68 | includepaths = [] 69 | if not options.includepath is None: 70 | includepaths += options.includepath 71 | 72 | buildfile = open('build.ninja', 'w') 73 | self.writer = syntax.Writer(buildfile) 74 | 75 | self.writer.variable('ninja_required_version', '1.3') 76 | self.writer.newline() 77 | 78 | self.writer.comment('configure.py arguments') 79 | self.writer.variable('configure_args', ' '.join(sys.argv[1:])) 80 | self.writer.newline() 81 | 82 | self.writer.comment('configure options') 83 | self.writer.variable('configure_target', self.target.platform) 84 | self.writer.variable('configure_host', self.host.platform) 85 | 86 | env_keys = set(['CC', 'AR', 'LINK', 'CFLAGS', 'ARFLAGS', 'LINKFLAGS']) 87 | configure_env = dict((key, os.environ[key]) for key in os.environ if key in env_keys) 88 | if configure_env: 89 | config_str = ' '.join([key + '=' + pipes.quote(configure_env[key]) for key in configure_env]) 90 | self.writer.variable('configure_env', config_str + '$ ') 91 | 92 | if variables is None: 93 | variables = {} 94 | if not isinstance(variables, dict): 95 | variables = dict(variables) 96 | 97 | if options.monolithic: 98 | variables['monolithic'] = True 99 | if options.coverage: 100 | variables['coverage'] = True 101 | if options.lto: 102 | variables['lto'] = True 103 | if self.subninja != '': 104 | variables['internal_deps'] = True 105 | 106 | self.toolchain = toolchain.make_toolchain(self.host, self.target, options.toolchain) 107 | self.toolchain.buildprefs = options.buildprefs 108 | self.toolchain.initialize(project, archs, configs, includepaths, dependlibs, libpaths, variables, self.subninja) 109 | 110 | self.writer.variable('configure_toolchain', self.toolchain.name()) 111 | self.writer.variable('configure_archs', archs) 112 | self.writer.variable('configure_configs', configs) 113 | self.writer.newline() 114 | 115 | self.toolchain.write_variables(self.writer) 116 | if self.subninja == '': 117 | self.toolchain.write_rules(self.writer) 118 | 119 | def target(self): 120 | return self.target 121 | 122 | def host(self): 123 | return self.host 124 | 125 | def toolchain(self): 126 | return self.toolchain 127 | 128 | def writer(self): 129 | return self.writer 130 | 131 | def is_subninja(self): 132 | return self.subninja != '' 133 | 134 | def skip_tests(self): 135 | return self.notests 136 | 137 | def lib(self, module, sources, libname = None, basepath = None, configs = None, includepaths = None, variables = None): 138 | return self.toolchain.lib(self.writer, module, sources, libname, basepath, configs, includepaths, variables) 139 | 140 | def sharedlib(self, module, sources, libname = None, basepath = None, configs = None, includepaths = None, libpaths = None, implicit_deps = None, dependlibs = None, libs = None, frameworks = None, variables = None): 141 | return self.toolchain.sharedlib(self.writer, module, sources, libname, basepath, configs, includepaths, libpaths, implicit_deps, dependlibs, libs, frameworks, variables) 142 | 143 | def bin(self, module, sources, binname, basepath = None, configs = None, includepaths = None, libpaths = None, implicit_deps = None, dependlibs = None, libs = None, frameworks = None, variables = None): 144 | return self.toolchain.bin(self.writer, module, sources, binname, basepath, configs, includepaths, libpaths, implicit_deps, dependlibs, libs, frameworks, variables) 145 | 146 | def app(self, module, sources, binname, basepath = None, configs = None, includepaths = None, libpaths = None, implicit_deps = None, dependlibs = None, libs = None, frameworks = None, variables = None, resources = None): 147 | return self.toolchain.app(self.writer, module, sources, binname, basepath, configs, includepaths, libpaths, implicit_deps, dependlibs, libs, frameworks, variables, resources) 148 | 149 | def test_includepaths(self): 150 | #TODO: This is ugly 151 | if self.project == "foundation": 152 | return ['test'] 153 | foundation_path = os.path.join('..', 'foundation_lib') 154 | if not os.path.isfile(os.path.join(foundation_path, 'foundation', 'foundation.h')): 155 | foundation_path = os.path.join('..', 'foundation') 156 | return ['test', os.path.join(foundation_path, 'test')] 157 | 158 | def test_monolithic(self): 159 | return self.toolchain.is_monolithic() 160 | -------------------------------------------------------------------------------- /build/ninja/platform.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | """Ninja platform abstraction""" 4 | 5 | import sys 6 | 7 | def supported_platforms(): 8 | return [ 'windows', 'linux', 'macos', 'bsd', 'ios', 'android', 'raspberrypi', 'tizen', 'sunos', 'haiku' ] 9 | 10 | class Platform(object): 11 | def __init__(self, platform): 12 | self.platform = platform 13 | if self.platform is None: 14 | self.platform = sys.platform 15 | if self.platform.startswith('linux'): 16 | self.platform = 'linux' 17 | elif self.platform.startswith('darwin'): 18 | self.platform = 'macos' 19 | elif self.platform.startswith('macos'): 20 | self.platform = 'macos' 21 | elif self.platform.startswith('win'): 22 | self.platform = 'windows' 23 | elif 'bsd' in self.platform or self.platform.startswith('dragonfly'): 24 | self.platform = 'bsd' 25 | elif self.platform.startswith('ios'): 26 | self.platform = 'ios' 27 | elif self.platform.startswith('android'): 28 | self.platform = 'android' 29 | elif self.platform.startswith('raspberry'): 30 | self.platform = 'raspberrypi' 31 | elif self.platform.startswith('tizen'): 32 | self.platform = 'tizen' 33 | elif self.platform.startswith('sunos'): 34 | self.platform = 'sunos' 35 | elif self.platform.startswith('haiku'): 36 | self.platform = 'haiku' 37 | 38 | def platform(self): 39 | return self.platform 40 | 41 | def is_linux(self): 42 | return self.platform == 'linux' 43 | 44 | def is_windows(self): 45 | return self.platform == 'windows' 46 | 47 | def is_macos(self): 48 | return self.platform == 'macos' 49 | 50 | def is_bsd(self): 51 | return self.platform == 'bsd' 52 | 53 | def is_ios(self): 54 | return self.platform == 'ios' 55 | 56 | def is_android(self): 57 | return self.platform == 'android' 58 | 59 | def is_raspberrypi(self): 60 | return self.platform == 'raspberrypi' 61 | 62 | def is_tizen(self): 63 | return self.platform == 'tizen' 64 | 65 | def is_sunos(self): 66 | return self.platform == 'sunos' 67 | 68 | def is_haiku(self): 69 | return self.platform == 'haiku' 70 | 71 | def get(self): 72 | return self.platform 73 | -------------------------------------------------------------------------------- /build/ninja/plist.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """PList utility""" 4 | 5 | import argparse 6 | import os 7 | import subprocess 8 | import re 9 | import unicodedata 10 | 11 | def normalize_char(c): 12 | try: 13 | UNICODE_EXISTS = bool(type(unicode)) 14 | except NameError: 15 | unicode = lambda s: str(s) 16 | try: 17 | cname = unicodedata.name( unicode(c) ) 18 | cname = cname[:cname.index( ' WITH' )] 19 | return unicodedata.lookup( cname ) 20 | except ( ValueError, KeyError ): 21 | return c 22 | 23 | def normalize_string(s): 24 | return ''.join( normalize_char(c) for c in s ) 25 | 26 | def replace_var( str, var, val ): 27 | if str.find( '$(' + var + ')' ) != -1: 28 | return str.replace( '$(' + var + ')', val ) 29 | if str.find( '${' + var + '}' ) != -1: 30 | return str.replace( '${' + var + '}', val ) 31 | return str 32 | 33 | 34 | parser = argparse.ArgumentParser( description = 'PList utility for Ninja builds' ) 35 | parser.add_argument( 'files', 36 | metavar = 'file', type=open, nargs='+', 37 | help = 'Source plist file' ) 38 | parser.add_argument( '--exename', type=str, 39 | help = 'Executable name', 40 | default = [] ) 41 | parser.add_argument( '--prodname', type=str, 42 | help = 'Product name', 43 | default = [] ) 44 | parser.add_argument( '--bundle', type=str, 45 | help = 'Bundle identifier', 46 | default = [] ) 47 | parser.add_argument( '--output', type=str, 48 | help = 'Output path', 49 | default = [] ) 50 | parser.add_argument( '--target', type=str, 51 | help = 'Target OS', 52 | default = [] ) 53 | parser.add_argument( '--deploymenttarget', type=str, 54 | help = 'Target OS version', 55 | default = [] ) 56 | options = parser.parse_args() 57 | 58 | if not options.exename: 59 | options.exename = 'unknown' 60 | if not options.prodname: 61 | options.prodname = 'unknown' 62 | if not options.target: 63 | options.target = 'macos' 64 | if not options.deploymenttarget: 65 | if options.target == 'macos': 66 | options.deploymenttarget = '12.0' 67 | else: 68 | options.deploymenttarget = '10.0' 69 | 70 | buildversion = subprocess.check_output( [ 'sw_vers', '-buildVersion' ] ).decode().strip() 71 | 72 | #Merge input plists using first file as base 73 | lines = [] 74 | for f in options.files: 75 | _, extension = os.path.splitext(f.name) 76 | if extension != '.plist': 77 | continue 78 | if lines == []: 79 | lines += [ line.strip( '\n\r' ) for line in f ] 80 | else: 81 | mergelines = [ line.strip( '\n\r' ) for line in f ] 82 | for i in range( 0, len( mergelines ) ): 83 | if re.match( '^$', mergelines[i] ): 84 | break 85 | if re.match( '^$', mergelines[i] ): 86 | for j in range( 0, len( lines ) ): 87 | if re.match( '^$', lines[j] ): 88 | for k in range( i+1, len( mergelines ) ): 89 | if re.match( '^$', mergelines[k] ): 90 | break 91 | lines.insert( j+(k-(i+1)), mergelines[k] ) 92 | break 93 | break 94 | 95 | #Parse input plist to get package type and signature 96 | bundle_package_type = 'APPL' 97 | bundle_signature = '????' 98 | 99 | for i in range( 0, len( lines ) ): 100 | if 'CFBundlePackageType' in lines[i]: 101 | match = re.match( '^.*>(.*)<.*$', lines[i+1] ) 102 | if match: 103 | bundle_package_type = match.group(1) 104 | if 'CFBundleSignature' in lines[i]: 105 | match = re.match( '^.*>(.*)<.*$', lines[i+1] ) 106 | if match: 107 | bundle_signature = match.group(1) 108 | 109 | #Write package type and signature to PkgInfo in output path 110 | with open( os.path.join( os.path.dirname( options.output ), 'PkgInfo' ), 'w' ) as pkginfo_file: 111 | pkginfo_file.write( bundle_package_type + bundle_signature ) 112 | pkginfo_file.close() 113 | 114 | #insert os version 115 | for i in range( 0, len( lines ) ): 116 | if re.match( '^$', lines[i] ): 117 | lines.insert( i+1, '\tBuildMachineOSBuild' ) 118 | lines.insert( i+2, '\t' + buildversion + '' ) 119 | break 120 | 121 | #replace build variables name 122 | for i in range( 0, len( lines ) ): 123 | lines[i] = replace_var( lines[i], 'EXECUTABLE_NAME', options.exename ) 124 | lines[i] = replace_var( lines[i], 'PRODUCT_NAME', options.prodname ) 125 | lines[i] = replace_var( lines[i], 'PRODUCT_NAME:rfc1034identifier', normalize_string( options.exename ).lower() ) 126 | lines[i] = replace_var( lines[i], 'PRODUCT_NAME:c99extidentifier', normalize_string( options.exename ).lower().replace( '-', '_' ).replace( '.', '_' ) ) 127 | lines[i] = replace_var( lines[i], 'IOS_DEPLOYMENT_TARGET', options.deploymenttarget ) 128 | lines[i] = replace_var( lines[i], 'MACOSX_DEPLOYMENT_TARGET', options.deploymenttarget ) 129 | 130 | #replace bundle identifier if given 131 | if not options.bundle is None and options.bundle != '': 132 | for i in range( 0, len( lines ) ): 133 | if lines[i].find( 'CFBundleIdentifier' ) != -1: 134 | lines[i+1] = '\t' + normalize_string( options.bundle ) + '' 135 | break 136 | 137 | #add supported platform, minimum os version and requirements 138 | if options.target == 'ios': 139 | for i in range( 0, len( lines ) ): 140 | if 'CFBundleSignature' in lines[i]: 141 | lines.insert( i+2, '\tCFBundleSupportedPlatforms' ) 142 | lines.insert( i+3, '\t' ) 143 | lines.insert( i+4, '\t\tiPhoneOS' ) 144 | lines.insert( i+5, '\t' ) 145 | lines.insert( i+6, '\tMinimumOSVersion' ) 146 | lines.insert( i+7, '\t6.0' ) 147 | lines.insert( i+8, '\tUIDeviceFamily' ) 148 | lines.insert( i+9, '\t' ) 149 | lines.insert( i+10, '\t\t1' ) 150 | lines.insert( i+11, '\t\t2' ) 151 | lines.insert( i+12, '\t' ) 152 | break 153 | 154 | #add build info 155 | #DTCompiler 156 | #com.apple.compilers.llvm.clang.1_0 157 | #DTPlatformBuild 158 | #12B411 159 | #DTPlatformName 160 | #iphoneos 161 | #DTPlatformVersion 162 | #8.1 163 | #DTSDKBuild 164 | #12B411 165 | #DTSDKName 166 | #iphoneos8.1 167 | #DTXcode 168 | #0611 169 | #DTXcodeBuild 170 | #6A2008a 171 | 172 | #write final Info.plist in output path 173 | with open( options.output, 'w' ) as plist_file: 174 | for line in lines: 175 | #print lines[i] 176 | plist_file.write( line + '\n' ) 177 | plist_file.close() 178 | 179 | #run plutil -convert binary1 180 | sdk = 'iphoneos' 181 | platformpath = subprocess.check_output( [ 'xcrun', '--sdk', sdk, '--show-sdk-platform-path' ] ).decode().strip() 182 | localpath = platformpath + "/Developer/usr/bin:/Applications/Xcode.app/Contents/Developer/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin" 183 | plutil = "PATH=" + localpath + " " + subprocess.check_output( [ 'xcrun', '--sdk', sdk, '-f', 'plutil' ] ).decode().strip() 184 | plcommand = plutil + ' -convert binary1 ' + options.output 185 | os.system( plcommand ) 186 | -------------------------------------------------------------------------------- /build/ninja/syntax.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | """Python module for generating .ninja files. 4 | 5 | Note that this is emphatically not a required piece of Ninja; it's 6 | just a helpful utility for build-file-generation systems that already 7 | use Python. 8 | """ 9 | 10 | import textwrap 11 | 12 | def escape_path(word): 13 | return word.replace('$ ', '$$ ').replace(' ', '$ ').replace(':', '$:') 14 | 15 | class Writer(object): 16 | def __init__(self, output, width=78): 17 | self.output = output 18 | self.width = width 19 | 20 | def newline(self): 21 | self.output.write('\n') 22 | 23 | def comment(self, text): 24 | for line in textwrap.wrap(text, self.width - 2): 25 | self.output.write('# ' + line + '\n') 26 | 27 | def variable(self, key, value, indent=0): 28 | if value is None: 29 | return 30 | if isinstance(value, list): 31 | value = ' '.join(filter(None, value)) # Filter out empty strings. 32 | self._line('%s = %s' % (key, value), indent) 33 | 34 | def pool(self, name, depth): 35 | self._line('pool %s' % name) 36 | self.variable('depth', depth, indent=1) 37 | 38 | def rule(self, name, command, description=None, depfile=None, 39 | generator=False, pool=None, restat=False, rspfile=None, 40 | rspfile_content=None, deps=None): 41 | self._line('rule %s' % name) 42 | self.variable('command', command, indent=1) 43 | if description: 44 | self.variable('description', description, indent=1) 45 | if depfile: 46 | self.variable('depfile', depfile, indent=1) 47 | if generator: 48 | self.variable('generator', '1', indent=1) 49 | if pool: 50 | self.variable('pool', pool, indent=1) 51 | if restat: 52 | self.variable('restat', '1', indent=1) 53 | if rspfile: 54 | self.variable('rspfile', rspfile, indent=1) 55 | if rspfile_content: 56 | self.variable('rspfile_content', rspfile_content, indent=1) 57 | if deps: 58 | self.variable('deps', deps, indent=1) 59 | 60 | def build(self, outputs, rule, inputs=None, implicit=None, order_only=None, 61 | variables=None): 62 | outputs = self._as_list(outputs) 63 | out_outputs = [escape_path(x) for x in outputs] 64 | all_inputs = [escape_path(x) for x in self._as_list(inputs)] 65 | 66 | if implicit: 67 | implicit = [escape_path(x) for x in self._as_list(implicit)] 68 | all_inputs.append('|') 69 | all_inputs.extend(implicit) 70 | if order_only: 71 | order_only = [escape_path(x) for x in self._as_list(order_only)] 72 | all_inputs.append('||') 73 | all_inputs.extend(order_only) 74 | 75 | self._line('build %s: %s' % (' '.join(out_outputs), 76 | ' '.join([rule] + all_inputs))) 77 | 78 | if variables: 79 | if isinstance(variables, dict): 80 | iterator = iter(variables.items()) 81 | else: 82 | iterator = iter(variables) 83 | 84 | for key, val in iterator: 85 | self.variable(key, val, indent=1) 86 | 87 | return outputs 88 | 89 | def include(self, path): 90 | self._line('include %s' % path) 91 | 92 | def subninja(self, path): 93 | self._line('subninja %s' % path) 94 | 95 | def default(self, paths): 96 | self._line('default %s' % ' '.join(self._as_list(paths))) 97 | 98 | def _count_dollars_before_index(self, s, i): 99 | """Returns the number of '$' characters right in front of s[i].""" 100 | dollar_count = 0 101 | dollar_index = i - 1 102 | while dollar_index > 0 and s[dollar_index] == '$': 103 | dollar_count += 1 104 | dollar_index -= 1 105 | return dollar_count 106 | 107 | def _line(self, text, indent=0): 108 | """Write 'text' word-wrapped at self.width characters.""" 109 | leading_space = ' ' * indent 110 | while len(leading_space) + len(text) > self.width: 111 | # The text is too wide; wrap if possible. 112 | 113 | # Find the rightmost space that would obey our width constraint and 114 | # that's not an escaped space. 115 | available_space = self.width - len(leading_space) - len(' $') 116 | space = available_space 117 | while True: 118 | space = text.rfind(' ', 0, space) 119 | if (space < 0 or 120 | self._count_dollars_before_index(text, space) % 2 == 0): 121 | break 122 | 123 | if space < 0: 124 | # No such space; just use the first unescaped space we can find. 125 | space = available_space - 1 126 | while True: 127 | space = text.find(' ', space + 1) 128 | if (space < 0 or 129 | self._count_dollars_before_index(text, space) % 2 == 0): 130 | break 131 | if space < 0: 132 | # Give up on breaking. 133 | break 134 | 135 | self.output.write(leading_space + text[0:space] + ' $\n') 136 | text = text[space+1:] 137 | 138 | # Subsequent lines are continuations, so indent them. 139 | leading_space = ' ' * (indent+2) 140 | 141 | self.output.write(leading_space + text + '\n') 142 | 143 | def _as_list(self, input): 144 | if input is None: 145 | return [] 146 | if isinstance(input, list): 147 | return input 148 | return [input] 149 | 150 | 151 | def escape(string): 152 | """Escape a string such that it can be embedded into a Ninja file without 153 | further interpretation.""" 154 | assert '\n' not in string, 'Ninja syntax does not allow newlines' 155 | # We only have one special metacharacter: '$'. 156 | return string.replace('$', '$$') 157 | -------------------------------------------------------------------------------- /build/ninja/version.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """Version utility""" 4 | 5 | import subprocess 6 | import os 7 | import sys 8 | 9 | def generate_version_string(libname): 10 | 11 | version_numbers = [] 12 | tokens = [] 13 | 14 | gitcmd = 'git' 15 | if sys.platform.startswith('win'): 16 | gitcmd = 'git.exe' 17 | try: 18 | git_version = subprocess.check_output( [ gitcmd, 'describe', '--tags', '--long' ], stderr = subprocess.STDOUT ).strip() 19 | tokens = git_version.decode().split( '-' ) 20 | version_numbers = tokens[0].split( '.' ) 21 | except Exception: 22 | pass 23 | 24 | version_major = "0" 25 | version_minor = "0" 26 | version_revision = "1" 27 | version_build = "0" 28 | version_scm = "0" 29 | 30 | if version_numbers and len( version_numbers ) > 2: 31 | version_major = version_numbers[0] 32 | version_minor = version_numbers[1] 33 | version_revision = version_numbers[2] 34 | 35 | if tokens and len( tokens ) > 2: 36 | version_build = tokens[1] 37 | version_scm = tokens[2][1:] 38 | 39 | module = "" 40 | if not libname == "foundation": 41 | module = "_module" 42 | 43 | source = """/* ****** AUTOMATICALLY GENERATED, DO NOT EDIT ****** 44 | This file is generated from the git describe command. 45 | Run the configure script to regenerate this file */ 46 | 47 | #include 48 | #include <""" + libname + "/" + libname + """.h> 49 | 50 | version_t 51 | """ + libname + module + """_version(void) { 52 | """ 53 | source += " return version_make(" + version_major + ", " + version_minor + ", " + version_revision + ", " + version_build + ", 0x" + version_scm + ");\n}\n" 54 | return source 55 | 56 | def read_version_string(input_path): 57 | try: 58 | file = open( os.path.join( input_path, 'version.c' ), "r" ) 59 | str = file.read() 60 | file.close() 61 | except IOError: 62 | str = "" 63 | return str 64 | 65 | def write_version_string(output_path, str): 66 | file = open( os.path.join( output_path, 'version.c' ), "w" ) 67 | file.write( str ) 68 | file.close 69 | 70 | def generate_version(libname, output_path): 71 | generated = generate_version_string(libname) 72 | if generated == None: 73 | return 74 | previous = read_version_string(output_path) 75 | 76 | if generated != previous: 77 | write_version_string(output_path, generated) 78 | 79 | if __name__ == "__main__": 80 | generate_version(sys.argv[1], sys.argv[2]) 81 | -------------------------------------------------------------------------------- /build/ninja/vslocate.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """Locate Visual Studio installations with Visual Studio Setup Configuration utility DLL""" 4 | 5 | import os 6 | import ctypes 7 | 8 | def get_vs_installations(): 9 | 10 | class ISetupInstanceVTable(ctypes.Structure): 11 | """Class matching VisualStudio Setup package ISetupInstance vtable""" 12 | pass 13 | 14 | class ISetupInstance(ctypes.Structure): 15 | """COM interface for ISetupInstance""" 16 | _fields_ = [('vtable', ctypes.POINTER(ISetupInstanceVTable))] 17 | 18 | class IEnumSetupInstancesVTable(ctypes.Structure): 19 | """Class matching VisualStudio Setup package IEnumSetupInstances vtable""" 20 | pass 21 | 22 | class IEnumSetupInstances(ctypes.Structure): 23 | """COM interface for IEnumSetupInstances""" 24 | _fields_ = [('vtable', ctypes.POINTER(IEnumSetupInstancesVTable))] 25 | 26 | class ISetupConfigurationVTable(ctypes.Structure): 27 | """Class matching VisualStudio Setup package ISetupConfiguration vtable""" 28 | pass 29 | 30 | class ISetupConfiguration(ctypes.Structure): 31 | """COM interface for ISetupConfiguration""" 32 | _fields_ = [('vtable', ctypes.POINTER(ISetupConfigurationVTable))] 33 | 34 | proto_get_installation_path = ctypes.WINFUNCTYPE( 35 | ctypes.c_int, 36 | ctypes.POINTER(ISetupInstance), 37 | ctypes.POINTER(ctypes.c_wchar_p)) 38 | 39 | proto_get_installation_version = ctypes.WINFUNCTYPE( 40 | ctypes.c_int, 41 | ctypes.POINTER(ISetupInstance), 42 | ctypes.POINTER(ctypes.c_wchar_p)) 43 | 44 | ISetupInstanceVTable._fields_ = ( 45 | ('QueryInterface', ctypes.c_void_p), 46 | ('AddRef', ctypes.c_void_p), 47 | ('Release', ctypes.c_void_p), 48 | ('GetInstanceId', ctypes.c_void_p), 49 | ('GetInstallDate', ctypes.c_void_p), 50 | ('GetInstallationName', ctypes.c_void_p), 51 | ('GetInstallationPath', proto_get_installation_path), 52 | ('GetInstallationVersion', proto_get_installation_version), 53 | ('GetDisplayName', ctypes.c_void_p), 54 | ('GetDescription', ctypes.c_void_p), 55 | ('ResolvePath', ctypes.c_void_p)) 56 | 57 | proto_next = ctypes.WINFUNCTYPE( 58 | ctypes.c_int, 59 | ctypes.POINTER(IEnumSetupInstances), 60 | ctypes.c_int, 61 | ctypes.POINTER(ctypes.POINTER(ISetupInstance)), 62 | ctypes.POINTER(ctypes.c_int)) 63 | 64 | IEnumSetupInstancesVTable._fields_ = ( 65 | ('QueryInterface', ctypes.c_void_p), 66 | ('AddRef', ctypes.c_void_p), 67 | ('Release', ctypes.c_void_p), 68 | ('Next', proto_next), 69 | ('Skip', ctypes.c_void_p), 70 | ('Reset', ctypes.c_void_p), 71 | ('Clone', ctypes.c_void_p)) 72 | 73 | proto_enum_instances = ctypes.WINFUNCTYPE( 74 | ctypes.c_int, 75 | ctypes.POINTER(ISetupConfiguration), 76 | ctypes.POINTER(ctypes.POINTER(IEnumSetupInstances))) 77 | 78 | ISetupConfigurationVTable._fields_ = ( 79 | ('QueryInterface', ctypes.c_void_p), 80 | ('AddRef', ctypes.c_void_p), 81 | ('Release', ctypes.c_void_p), 82 | ('EnumInstances', proto_enum_instances), 83 | ('GetInstanceForCurrentProcess', ctypes.c_void_p), 84 | ('GetInstanceForPath', ctypes.c_void_p)) 85 | 86 | proto_get_setup_configuration = ctypes.WINFUNCTYPE( 87 | ctypes.c_int, 88 | ctypes.POINTER(ctypes.POINTER(ISetupConfiguration)), 89 | ctypes.c_void_p) 90 | 91 | installations = [] 92 | dll = None 93 | 94 | dll_path = os.path.expandvars("$ProgramData\\Microsoft\\VisualStudio\\Setup\\x64\\Microsoft.VisualStudio.Setup.Configuration.Native.dll") 95 | try: 96 | dll = ctypes.WinDLL(dll_path) 97 | except OSError as e: 98 | #print("Failed to load Visual Studio setup configuration DLL: " + str(e)) 99 | return installations 100 | 101 | params_get_setup_configuration = (1, "configuration", 0), (1, "reserved", 0), 102 | 103 | get_setup_configuration = proto_get_setup_configuration(("GetSetupConfiguration", dll), params_get_setup_configuration) 104 | 105 | configuration = ctypes.POINTER(ISetupConfiguration)() 106 | reserved = ctypes.c_void_p(0) 107 | 108 | result = get_setup_configuration(ctypes.byref(configuration), reserved) 109 | if result != 0: 110 | #print("Failed to get setup configuration: " + str(result)) 111 | return installations 112 | 113 | enum_instances = configuration.contents.vtable.contents.EnumInstances 114 | 115 | enum_setup_instances = ctypes.POINTER(IEnumSetupInstances)() 116 | result = enum_instances(configuration, ctypes.byref(enum_setup_instances)) 117 | if result != 0: 118 | #print("Failed to enum setup instances: " + str(result)) 119 | return installations 120 | 121 | 122 | setup_instance = ctypes.POINTER(ISetupInstance)() 123 | fetched = ctypes.c_int(0) 124 | 125 | while True: 126 | next = enum_setup_instances.contents.vtable.contents.Next 127 | result = next(enum_setup_instances, 1, ctypes.byref(setup_instance), ctypes.byref(fetched)) 128 | if result == 1 or fetched == 0: 129 | break 130 | if result != 0: 131 | #print("Failed to get next setup instance: " + str(result)) 132 | break 133 | 134 | version = ctypes.c_wchar_p() 135 | path = ctypes.c_wchar_p() 136 | 137 | get_installation_version = setup_instance.contents.vtable.contents.GetInstallationVersion 138 | get_installation_path = setup_instance.contents.vtable.contents.GetInstallationPath 139 | 140 | result = get_installation_version(setup_instance, ctypes.byref(version)) 141 | if result != 0: 142 | #print("Failed to get setup instance version: " + str(result)) 143 | break 144 | 145 | result = get_installation_path(setup_instance, ctypes.byref(path)) 146 | if result != 0: 147 | #print("Failed to get setup instance version: " + str(result)) 148 | break 149 | 150 | installations.append((version.value, path.value)) 151 | 152 | return installations 153 | 154 | 155 | if __name__ == "__main__": 156 | 157 | installations = get_vs_installations() 158 | 159 | for version, path in installations: 160 | print(version + " " + path) 161 | -------------------------------------------------------------------------------- /build/ninja/xcode.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """Ninja toolchain abstraction for XCode toolchain""" 4 | 5 | import os 6 | import subprocess 7 | 8 | import toolchain 9 | import syntax 10 | 11 | def make_target(toolchain, host, target): 12 | return XCode(toolchain, host, target) 13 | 14 | class XCode(object): 15 | def __init__(self, toolchain, host, target): 16 | self.toolchain = toolchain 17 | self.host = host 18 | self.target = target 19 | 20 | def initialize_toolchain(self): 21 | self.organisation = '' 22 | self.bundleidentifier = '' 23 | self.provisioning = '' 24 | if self.target.is_macos(): 25 | self.deploymenttarget = '12.0' 26 | elif self.target.is_ios(): 27 | self.deploymenttarget = '15.0' 28 | 29 | def build_toolchain(self): 30 | if self.target.is_macos(): 31 | sdk = 'macosx' 32 | deploytarget = 'MACOSX_DEPLOYMENT_TARGET=' + self.deploymenttarget 33 | elif self.target.is_ios(): 34 | sdk = 'iphoneos' 35 | deploytarget = 'IPHONEOS_DEPLOYMENT_TARGET=' + self.deploymenttarget 36 | 37 | platformpath = toolchain.check_last_output(['xcrun', '--sdk', sdk, '--show-sdk-platform-path']) 38 | localpath = platformpath + "/Developer/usr/bin:/Applications/Xcode.app/Contents/Developer/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin" 39 | 40 | self.plist = "PATH=" + localpath + " " + toolchain.check_last_output(['xcrun', '--sdk', sdk, '-f', 'plutil']) 41 | self.xcassets = "PATH=" + localpath + " " + toolchain.check_last_output(['xcrun', '--sdk', sdk, '-f', 'actool']) 42 | self.xib = "PATH=" + localpath + " " + toolchain.check_last_output(['xcrun', '--sdk', sdk, '-f', 'ibtool']) 43 | self.dsymutil = "PATH=" + localpath + " " + toolchain.check_last_output(['xcrun', '--sdk', sdk, '-f', 'dsymutil']) 44 | 45 | self.plistcmd = 'build/ninja/plist.py --exename $exename --prodname $prodname --bundle $bundleidentifier --target $target --deploymenttarget $deploymenttarget --output $outpath $in' 46 | if self.target.is_macos(): 47 | self.xcassetscmd = 'mkdir -p $outpath && $xcassets --output-format human-readable-text --output-partial-info-plist $outplist' \ 48 | ' --app-icon AppIcon --launch-image LaunchImage --platform macosx --minimum-deployment-target ' + self.deploymenttarget + \ 49 | ' --target-device mac --compress-pngs --compile $outpath $in >/dev/null' 50 | self.xibcmd = '$xib --target-device mac --module $module --minimum-deployment-target ' + self.deploymenttarget + \ 51 | ' --output-partial-info-plist $outplist --auto-activate-custom-fonts' \ 52 | ' --output-format human-readable-text --compile $outpath $in' 53 | elif self.target.is_ios(): 54 | self.xcassetscmd = 'mkdir -p $outpath && $xcassets --output-format human-readable-text --output-partial-info-plist $outplist' \ 55 | ' --app-icon AppIcon --launch-image LaunchImage --platform iphoneos --minimum-deployment-target ' + self.deploymenttarget + \ 56 | ' --target-device iphone --target-device ipad --compress-pngs --compile $outpath $in >/dev/null' 57 | self.xibcmd = '$xib --target-device iphone --target-device ipad --module $module --minimum-deployment-target ' + self.deploymenttarget + \ 58 | ' --output-partial-info-plist $outplist --auto-activate-custom-fonts' \ 59 | ' --output-format human-readable-text --compile $outpath $in &> /dev/null ' 60 | self.dsymutilcmd = '$dsymutil $in -o $outpath' 61 | self.codesigncmd = 'build/ninja/codesign.py --target $target --prefs codesign.json --builddir $builddir --binname $binname --config $config --entitlements $entitlements $outpath' 62 | 63 | def parse_default_variables(self, variables): 64 | if not variables: 65 | return 66 | if isinstance(variables, dict): 67 | iterator = iter(variables.items()) 68 | else: 69 | iterator = iter(variables) 70 | for key, val in iterator: 71 | if key == 'deploymenttarget': 72 | self.deploymenttarget = val 73 | if key == 'organisation': 74 | self.organisation = val 75 | if key == 'bundleidentifier': 76 | self.bundleidentifier = val 77 | if key == 'provisioning': 78 | self.provisioning = val 79 | 80 | def parse_prefs(self, prefs): 81 | if self.target.is_ios() and 'ios' in prefs: 82 | iosprefs = prefs['ios'] 83 | if 'deploymenttarget' in iosprefs: 84 | self.deploymenttarget = iosprefs['deploymenttarget'] 85 | if 'organisation' in iosprefs: 86 | self.organisation = iosprefs['organisation'] 87 | if 'bundleidentifier' in iosprefs: 88 | self.bundleidentifier = iosprefs['bundleidentifier'] 89 | if 'provisioning' in iosprefs: 90 | self.provisioning = iosprefs['provisioning'] 91 | elif self.target.is_macos() and 'macos' in prefs: 92 | macosprefs = prefs['macos'] 93 | if 'deploymenttarget' in macosprefs: 94 | self.deploymenttarget = macosprefs['deploymenttarget'] 95 | if 'organisation' in macosprefs: 96 | self.organisation = macosprefs['organisation'] 97 | if 'bundleidentifier' in macosprefs: 98 | self.bundleidentifier = macosprefs['bundleidentifier'] 99 | if 'provisioning' in macosprefs: 100 | self.provisioning = macosprefs['provisioning'] 101 | 102 | def write_variables(self, writer): 103 | writer.variable('plist', self.plist) 104 | writer.variable('xcassets', self.xcassets) 105 | writer.variable('xib', self.xib) 106 | writer.variable('dsymutil', self.dsymutil) 107 | writer.variable('bundleidentifier', syntax.escape(self.bundleidentifier)) 108 | writer.variable('deploymenttarget', self.deploymenttarget) 109 | writer.variable('entitlements', 'none') 110 | 111 | def write_rules(self, writer): 112 | writer.rule('dsymutil', command = self.dsymutilcmd, description = 'DSYMUTIL $outpath') 113 | writer.rule('plist', command = self.plistcmd, description = 'PLIST $outpath') 114 | writer.rule('xcassets', command = self.xcassetscmd, description = 'XCASSETS $outpath') 115 | writer.rule('xib', command = self.xibcmd, description = 'XIB $outpath') 116 | writer.rule('codesign', command = self.codesigncmd, description = 'CODESIGN $outpath') 117 | 118 | def make_bundleidentifier(self, binname): 119 | return self.bundleidentifier.replace('$(binname)', binname) 120 | 121 | def app(self, toolchain, writer, module, archbins, outpath, binname, basepath, config, implicit_deps, resources, codesign): 122 | #Outputs 123 | builtbin = [] 124 | builtres = [] 125 | builtsym = [] 126 | 127 | #Paths 128 | builddir = os.path.join('$buildpath', config, 'app', binname) 129 | configpath = os.path.join(outpath, config) 130 | apppath = os.path.join(configpath, binname + '.app') 131 | dsympath = os.path.join(outpath, config, binname + '.dSYM') 132 | 133 | #Extract debug symbols from universal binary 134 | dsymcontentpath = os.path.join(dsympath, 'Contents') 135 | builtsym = writer.build([os.path.join(dsymcontentpath, 'Resources', 'DWARF', binname), os.path.join(dsymcontentpath, 'Resources', 'DWARF' ), os.path.join(dsymcontentpath, 'Resources'), os.path.join(dsymcontentpath, 'Info.plist'), dsymcontentpath, dsympath], 'dsymutil', archbins[config], variables = [('outpath', dsympath)]) 136 | 137 | #Copy final universal binary 138 | if self.target.is_ios(): 139 | builtbin = toolchain.copy(writer, archbins[config], os.path.join(apppath, toolchain.binprefix + binname + toolchain.binext)) 140 | else: 141 | builtbin = toolchain.copy(writer, archbins[config], os.path.join(apppath, 'Contents', 'MacOS', toolchain.binprefix + binname + toolchain.binext)) 142 | 143 | #Build resources 144 | if resources: 145 | has_resources = False 146 | 147 | #Lists of input plists and partial plist files produced by resources 148 | plists = [] 149 | assetsplists = [] 150 | xibplists = [] 151 | entitlements = [] 152 | 153 | #All resource output files 154 | outfiles = [] 155 | 156 | #First build everything except plist inputs 157 | for resource in resources: 158 | if resource.endswith('.xcassets'): 159 | if self.target.is_macos(): 160 | assetsvars = [('outpath', os.path.join(os.getcwd(), apppath, 'Contents', 'Resources'))] 161 | else: 162 | assetsvars = [('outpath', apppath)] 163 | outplist = os.path.join(os.getcwd(), builddir, os.path.splitext(os.path.basename(resource))[0] + '-xcassets.plist') 164 | assetsvars += [('outplist', outplist)] 165 | outfiles = [outplist] 166 | if self.target.is_macos(): 167 | outfiles += [os.path.join(os.getcwd(), apppath, 'Contents', 'Resources', 'AppIcon.icns')] 168 | elif self.target.is_ios(): 169 | pass #TODO: Need to list all icon and launch image files here 170 | assetsplists += writer.build(outfiles, 'xcassets', os.path.join(os.getcwd(), basepath, module, resource), variables = assetsvars) 171 | has_resources = True 172 | elif resource.endswith('.xib'): 173 | xibmodule = binname.replace('-', '_').replace('.', '_') 174 | if self.target.is_macos(): 175 | nibpath = os.path.join(apppath, 'Contents', 'Resources', os.path.splitext(os.path.basename(resource))[0] + '.nib') 176 | else: 177 | nibpath = os.path.join(apppath, os.path.splitext(os.path.basename(resource))[0] + '.nib') 178 | plistpath = os.path.join(builddir, os.path.splitext(os.path.basename(resource))[0] + '-xib.plist') 179 | xibplists += [plistpath] 180 | outfiles = [] 181 | if self.target.is_ios(): 182 | outfiles += [os.path.join(nibpath, 'objects.nib'), os.path.join(nibpath, 'objects-8.0+.nib'), os.path.join(nibpath, 'runtime.nib')] 183 | outfiles += [nibpath, plistpath] 184 | builtres += writer.build(outfiles, 'xib', os.path.join(os.getcwd(), basepath, module, resource), variables = [('outpath', nibpath), ('outplist', plistpath), ('module', xibmodule)]) 185 | has_resources = True 186 | elif resource.endswith('.plist'): 187 | plists += [os.path.join(os.getcwd(), basepath, module, resource)] 188 | elif resource.endswith('.entitlements'): 189 | entitlements += [os.path.join(os.getcwd(), basepath, module, resource)] 190 | 191 | #Extra output files/directories 192 | outfiles = [] 193 | if has_resources and self.target.is_macos(): 194 | outfiles += [os.path.join(apppath, 'Contents', 'Resources')] 195 | 196 | #Now build input plists appending partial plists created by previous resources 197 | if self.target.is_macos(): 198 | plistpath = os.path.join(apppath, 'Contents', 'Info.plist') 199 | pkginfopath = os.path.join(apppath, 'Contents', 'PkgInfo') 200 | else: 201 | plistpath = os.path.join(apppath, 'Info.plist') 202 | pkginfopath = os.path.join(apppath, 'PkgInfo') 203 | plistvars = [('exename', binname), ('prodname', binname), ('outpath', plistpath)] 204 | bundleidentifier = self.make_bundleidentifier(binname) 205 | if bundleidentifier != '': 206 | plistvars += [('bundleidentifier', bundleidentifier)] 207 | outfiles += [plistpath, pkginfopath] 208 | builtres += writer.build(outfiles, 'plist', plists + assetsplists + xibplists, implicit = [os.path.join( 'build', 'ninja', 'plist.py')], variables = plistvars) 209 | 210 | #Do code signing (might modify binary, but does not matter, nothing should have final binary as input anyway) 211 | if codesign: 212 | codesignvars = [('builddir', builddir), ('binname', binname), ('outpath', apppath), ('config', config)] 213 | if self.target.is_ios(): 214 | if self.provisioning != '': 215 | codesignvars += [('provisioning', self.provisioning)] 216 | writer.build([os.path.join(apppath, '_CodeSignature', 'CodeResources'), os.path.join(apppath, '_CodeSignature'), apppath], 'codesign', builtbin, implicit = builtres + [os.path.join('build', 'ninja', 'codesign.py')], variables = codesignvars) 217 | elif self.target.is_macos(): 218 | if self.provisioning != '': 219 | codesignvars += [('provisioning', self.provisioning)] 220 | if len(entitlements) > 0: 221 | codesignvars += [('entitlements', entitlements[0])] 222 | writer.build([os.path.join(apppath, 'Contents', '_CodeSignature', 'CodeResources'), os.path.join(apppath, 'Contents', '_CodeSignature'), os.path.join(apppath, 'Contents'), apppath], 'codesign', builtbin, implicit = builtres + [os.path.join('build', 'ninja', 'codesign.py')], variables = codesignvars) 223 | 224 | return builtbin + builtsym + builtres 225 | -------------------------------------------------------------------------------- /build/task.sublime-project: -------------------------------------------------------------------------------- 1 | { 2 | "folders": 3 | [ 4 | { 5 | "follow_symlinks": true, 6 | "path": "../task" 7 | }, 8 | { 9 | "follow_symlinks": true, 10 | "path": "../test" 11 | }, 12 | { 13 | "follow_symlinks": true, 14 | "path": "../tools" 15 | }, 16 | { 17 | "follow_symlinks": true, 18 | "path": "../doc" 19 | } 20 | ], 21 | "build_systems": 22 | [ 23 | { 24 | "name": "Ninja", 25 | "shell_cmd": "ninja", 26 | "working_dir": "${project_path:${folder:${file_path}}}/..", 27 | } 28 | ], 29 | "translate_tabs_to_spaces": false, 30 | "use_tab_stops": true, 31 | "trim_trailing_white_space_on_save": true, 32 | "settings": 33 | { 34 | "AStyleFormatter": 35 | { 36 | "options_default": 37 | { 38 | "style" : "attach", 39 | "pad-oper": true, 40 | "indent-switches" : false, 41 | "indent-cases" : true, 42 | "unpad-paren": true, 43 | "break-closing-brackets": true, 44 | "align-pointer": "type", 45 | "max-code-length": 100, 46 | "max-instatement-indent": 60, 47 | "break-after-logical": true 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /configure.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """Ninja build configurator for task library""" 4 | 5 | import sys 6 | import os 7 | import glob 8 | import shutil 9 | 10 | sys.path.insert(0, os.path.join('build', 'ninja')) 11 | 12 | import generator 13 | 14 | dependlibs = ['task', 'foundation'] 15 | 16 | generator = generator.Generator(project='task', dependlibs=dependlibs, variables=[ 17 | ('bundleidentifier', 'com.maniccoder.task.$(binname)')]) 18 | target = generator.target 19 | writer = generator.writer 20 | toolchain = generator.toolchain 21 | 22 | task_lib = generator.lib(module='task', sources=[ 23 | 'executor.c', 'fiber.c', 'scheduler.c', 'task.c', 'version.c']) 24 | 25 | if generator.skip_tests(): 26 | sys.exit() 27 | 28 | includepaths = generator.test_includepaths() 29 | 30 | extralibs = [] 31 | 32 | test_cases = [ 33 | 'task' 34 | ] 35 | if toolchain.is_monolithic() or target.is_ios() or target.is_android() or target.is_tizen(): 36 | # Build one fat binary with all test cases 37 | test_resources = [] 38 | test_extrasources = [] 39 | test_cases += ['all'] 40 | if target.is_ios(): 41 | test_resources = [os.path.join('all', 'ios', item) for item in [ 42 | 'test-all.plist', 'Images.xcassets', 'test-all.xib']] 43 | test_extrasources = [os.path.join('all', 'ios', 'viewcontroller.m')] 44 | elif target.is_android(): 45 | test_resources = [os.path.join('all', 'android', item) for item in [ 46 | 'AndroidManifest.xml', os.path.join( 47 | 'layout', 'main.xml'), os.path.join('values', 'strings.xml'), 48 | os.path.join('drawable-ldpi', 'icon.png'), os.path.join('drawable-mdpi', 49 | 'icon.png'), os.path.join('drawable-hdpi', 'icon.png'), 50 | os.path.join('drawable-xhdpi', 'icon.png'), os.path.join('drawable-xxhdpi', 51 | 'icon.png'), os.path.join('drawable-xxxhdpi', 'icon.png') 52 | ]] 53 | test_extrasources = [os.path.join('test', 'all', 'android', 'java', 'com', 'maniccoder', 'task', 'test', item) for item in [ 54 | 'TestActivity.java' 55 | ]] 56 | elif target.is_tizen(): 57 | test_resources = [os.path.join('all', 'tizen', item) for item in [ 58 | 'tizen-manifest.xml', os.path.join('res', 'tizenapp.png') 59 | ]] 60 | dependlibs = ['test'] + dependlibs 61 | if target.is_macos() or target.is_ios() or target.is_android() or target.is_tizen(): 62 | generator.app(module='', sources=[os.path.join(module, 'main.c') for module in test_cases] + test_extrasources, binname='test-task', 63 | basepath='test', implicit_deps=[task_lib], libs=dependlibs, dependlibs=dependlibs, resources=test_resources, includepaths=includepaths) 64 | else: 65 | generator.bin(module='', sources=[os.path.join(module, 'main.c') for module in test_cases] + test_extrasources, binname='test-task', 66 | basepath='test', implicit_deps=[task_lib], libs=dependlibs, dependlibs=dependlibs, resources=test_resources, includepaths=includepaths) 67 | else: 68 | # Build one binary per test case 69 | if not generator.is_subninja: 70 | generator.bin(module='all', sources=['main.c'], binname='test-all', basepath='test', 71 | implicit_deps=[task_lib], libs=dependlibs, dependlibs=dependlibs, includepaths=includepaths) 72 | dependlibs = ['test'] + dependlibs 73 | for test in test_cases: 74 | generator.bin(module=test, sources=['main.c'], binname='test-' + test, basepath='test', implicit_deps=[ 75 | task_lib], libs=dependlibs + extralibs, dependlibs=dependlibs, includepaths=includepaths) 76 | -------------------------------------------------------------------------------- /task/build.h: -------------------------------------------------------------------------------- 1 | /* build.h - Task library - Public Domain - 2013 Mattias Jansson 2 | * 3 | * This library provides a cross-platform library in C11 providing 4 | * task-based parallellism for projects based on our foundation library. 5 | * 6 | * The latest source code maintained by Mattias Jansson is always available at 7 | * 8 | * https://github.com/mjansson/task_lib 9 | * 10 | * The foundation library source code maintained by Mattias Jansson is always available at 11 | * 12 | * https://github.com/mjansson/foundation_lib 13 | * 14 | * This library is put in the public domain; you can redistribute it and/or modify it without any restrictions. 15 | * 16 | */ 17 | 18 | #pragma once 19 | 20 | /*! \file build.h 21 | Build setup */ 22 | 23 | #include 24 | 25 | #include 26 | 27 | /*! \define BUILD_TASK_ENABLE_DEBUG_LOG 28 | Enable debug log output during task scheduling and execution */ 29 | #define BUILD_TASK_ENABLE_DEBUG_LOG 0 30 | 31 | /*! \define BUILD_TASK_ENABLE_STATISTICS 32 | Enable statistics collection during task scheduling and execution */ 33 | #define BUILD_TASK_ENABLE_STATISTICS 1 34 | -------------------------------------------------------------------------------- /task/executor.c: -------------------------------------------------------------------------------- 1 | /* executor.c - Task library - Public Domain - 2013 Mattias Jansson 2 | * 3 | * This library provides a cross-platform library in C11 providing 4 | * task-based parallellism for projects based on our foundation library. 5 | * 6 | * The latest source code maintained by Mattias Jansson is always available at 7 | * 8 | * https://github.com/mjansson/task_lib 9 | * 10 | * The foundation library source code maintained by Mattias Jansson is always available at 11 | * 12 | * https://github.com/mjansson/foundation_lib 13 | * 14 | * This library is put in the public domain; you can redistribute it and/or modify it without any restrictions. 15 | * 16 | */ 17 | 18 | #include "executor.h" 19 | #include "scheduler.h" 20 | #include "fiber.h" 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | 31 | #if FOUNDATION_PLATFORM_APPLE 32 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 33 | #endif 34 | #if FOUNDATION_PLATFORM_POSIX 35 | #ifndef _XOPEN_SOURCE 36 | #define _XOPEN_SOURCE 700 37 | #endif 38 | #include 39 | #endif 40 | 41 | FOUNDATION_DECLARE_THREAD_LOCAL(task_executor_t*, task_executor_current, nullptr) 42 | 43 | extern task_executor_t* 44 | task_executor_thread_current(void); 45 | 46 | task_executor_t* 47 | task_executor_thread_current(void) { 48 | return get_thread_task_executor_current(); 49 | } 50 | 51 | static task_fiber_t* 52 | task_executor_next_free_fiber(task_executor_t* executor) { 53 | task_fiber_t* fiber; 54 | if (executor->fiber_free) { 55 | fiber = executor->fiber_free; 56 | executor->fiber_free = nullptr; 57 | return fiber; 58 | } 59 | 60 | mutex_lock(executor->fiber_finished_lock); 61 | fiber = executor->fiber_finished; 62 | executor->fiber_finished = fiber ? fiber->fiber_next : nullptr; 63 | mutex_unlock(executor->fiber_finished_lock); 64 | if (fiber) { 65 | fiber->fiber_next = nullptr; 66 | return fiber; 67 | } 68 | 69 | return task_scheduler_next_free_fiber(executor->scheduler); 70 | } 71 | 72 | static void 73 | task_executor_fiber(task_executor_t* executor, task_fiber_t* self_fiber) { 74 | task_scheduler_t* scheduler = executor->scheduler; 75 | 76 | #if FOUNDATION_PLATFORM_POSIX 77 | // Make sure executor fiber resumes here 78 | ucontext_t* context = self_fiber->context; 79 | getcontext(context); 80 | #endif 81 | 82 | while (atomic_load32(&scheduler->running, memory_order_acquire)) { 83 | if (executor->fiber_waiting_release) { 84 | task_fiber_t* fiber = executor->fiber_waiting_release; 85 | executor->fiber_waiting_release = nullptr; 86 | 87 | // Release the artificial count done to make sure fiber was not prematurely 88 | // resumed before the switch back to executor was complete 89 | if (atomic_decr32(fiber->waiting_counter, memory_order_relaxed) == 0) { 90 | // All subtasks completed while switching to the executor fiber when 91 | // yielding this fiber (task), switch back and continue execution 92 | task_fiber_t* fiber_waiting = task_scheduler_pop_fiber_waiting(scheduler, fiber->waiting_counter); 93 | FOUNDATION_ASSERT(fiber_waiting == fiber); 94 | FOUNDATION_ASSERT_MSG(!fiber_waiting->fiber_pending_finished, 95 | "Internal fiber failure, continuation fiber already has pending finished fiber"); 96 | FOUNDATION_ASSERT_MSG( 97 | fiber_waiting->state == TASK_FIBER_YIELD, 98 | "Internal fiber failure, waiting fiber not in yield state when resuming in fiber"); 99 | 100 | task_fiber_switch(self_fiber, fiber_waiting); 101 | } 102 | } 103 | 104 | // Grab the next pending task, either a new task or a task to resume 105 | task_t task; 106 | if (task_scheduler_next_task(scheduler, &task)) { 107 | if (self_fiber->fiber_pending_finished) { 108 | // This will be reached once a fiber has finished executing a task and run out of 109 | // pending task recursions - the original task fiber is put in the executor pending 110 | // finished and context is switched back to this fiber to clean up 111 | task_executor_finished_fiber(executor, self_fiber->fiber_pending_finished); 112 | self_fiber->fiber_pending_finished = nullptr; 113 | } 114 | 115 | // This is a new task, grab a free fiber 116 | task_fiber_t* fiber = task_executor_next_free_fiber(executor); 117 | FOUNDATION_ASSERT_MSG((fiber->state == TASK_FIBER_FREE) || (fiber->state == TASK_FIBER_NOT_INITIALIZED), 118 | "Internal fiber failure, free fiber not in free state"); 119 | task_fiber_initialize(fiber); 120 | 121 | // Switch to the task fiber to execute it 122 | fiber->task = task; 123 | fiber->state = TASK_FIBER_RUNNING; 124 | task_fiber_switch(self_fiber, fiber); 125 | } else { 126 | // Task queue is empty, wait for signal 127 | if (atomic_load32(&scheduler->running, memory_order_relaxed)) 128 | semaphore_try_wait(&scheduler->signal, 10); 129 | } 130 | } 131 | 132 | if (self_fiber->fiber_pending_finished) { 133 | task_executor_finished_fiber(executor, self_fiber->fiber_pending_finished); 134 | self_fiber->fiber_pending_finished = nullptr; 135 | } 136 | 137 | task_fiber_switch(nullptr, self_fiber->fiber_return); 138 | } 139 | 140 | #if FOUNDATION_PLATFORM_WINDOWS 141 | extern void 142 | task_fiber_initialize_for_executor_thread(task_executor_t* executor, task_fiber_t* fiber, 143 | void (*executor_function)(long, long, long, long, task_executor_t*, 144 | task_fiber_t*)); 145 | 146 | static FOUNDATION_NOINLINE void STDCALL 147 | task_executor_trampoline(long ecx, long edx, long r8, long r9, task_executor_t* executor, task_fiber_t* self_fiber) { 148 | FOUNDATION_UNUSED(ecx, edx, r8, r9); 149 | 150 | #elif FOUNDATION_PLATFORM_POSIX 151 | extern void 152 | task_fiber_initialize_for_executor_thread(task_executor_t* executor, task_fiber_t* fiber, 153 | void (*executor_function)(int, int, int, int, int, int, int, int, int, int)); 154 | 155 | static FOUNDATION_NOINLINE void 156 | task_executor_trampoline(int r0, int r1, int r2, int r3, int r4, int r5, int executor_low, int executor_high, 157 | int fiber_low, int fiber_high) { 158 | FOUNDATION_UNUSED(r0, r1, r2, r3, r4, r5); 159 | // Reconstruct 64bit pointers 160 | task_executor_t* executor = (void*)(((uintptr_t)((uint)executor_high) << 32ULL) | (uintptr_t)((uint)executor_low)); 161 | task_fiber_t* self_fiber = (void*)(((uintptr_t)((uint)fiber_high) << 32ULL) | (uintptr_t)((uint)fiber_low)); 162 | 163 | #else 164 | #error not implemented 165 | #endif 166 | atomic_thread_fence_sequentially_consistent(); 167 | 168 | self_fiber->state = TASK_FIBER_EXECUTOR; 169 | 170 | task_executor_fiber(executor, self_fiber); 171 | } 172 | 173 | void* 174 | task_executor_thread(void* arg) { 175 | task_executor_t* executor = arg; 176 | set_thread_task_executor_current(executor); 177 | 178 | size_t total_fiber_size = 179 | executor->scheduler->fiber_size + executor->scheduler->fiber_context_size + executor->scheduler->fiber_tib_size; 180 | executor->self_fiber = memory_allocate(HASH_TASK, total_fiber_size, 0, MEMORY_PERSISTENT); 181 | executor->self_fiber->context = pointer_offset(executor->self_fiber, executor->scheduler->fiber_size); 182 | executor->self_fiber->tib = pointer_offset(executor->self_fiber->context, executor->scheduler->fiber_context_size); 183 | 184 | // Grab a fiber to get a clean contained stack space 185 | task_fiber_t* executor_fiber = task_scheduler_next_free_fiber(executor->scheduler); 186 | task_fiber_initialize_for_executor_thread(executor, executor_fiber, task_executor_trampoline); 187 | 188 | // Store current thread context 189 | if (!task_fiber_initialize_from_current_thread(executor->self_fiber)) 190 | exception_raise_abort(); 191 | 192 | executor = get_thread_task_executor_current(); 193 | if (atomic_load32(&executor->scheduler->running, memory_order_acquire)) 194 | task_fiber_switch(executor->self_fiber, executor_fiber); 195 | 196 | executor = get_thread_task_executor_current(); 197 | memory_deallocate(executor->self_fiber); 198 | executor->self_fiber = nullptr; 199 | 200 | #if BUILD_ENABLE_ERROR_CONTEXT 201 | error_context_set(nullptr); 202 | #endif 203 | 204 | return nullptr; 205 | } 206 | 207 | extern void 208 | task_executor_finished_fiber_internal(task_executor_t* executor, task_fiber_t* fiber); 209 | 210 | void 211 | task_executor_finished_fiber_internal(task_executor_t* executor, task_fiber_t* fiber) { 212 | FOUNDATION_ASSERT_MSG(fiber->stack, "Internal fiber failure, executor control fiber marked as free"); 213 | FOUNDATION_ASSERT_MSG(fiber->state == TASK_FIBER_FINISHED, 214 | "Internal fiber failure, finished fiber not in finished state"); 215 | fiber->state = TASK_FIBER_FREE; 216 | 217 | #if BUILD_ENABLE_ERROR_CONTEXT 218 | memory_deallocate(fiber->error_context); 219 | fiber->error_context = nullptr; 220 | #endif 221 | 222 | if (!executor->fiber_free) { 223 | executor->fiber_free = fiber; 224 | return; 225 | } 226 | 227 | mutex_lock(executor->fiber_finished_lock); 228 | fiber->fiber_next = executor->fiber_finished; 229 | executor->fiber_finished = fiber; 230 | mutex_unlock(executor->fiber_finished_lock); 231 | } 232 | 233 | void 234 | task_executor_finished_fiber(task_executor_t* executor, task_fiber_t* fiber) { 235 | FOUNDATION_ASSERT_MSG(fiber->stack, "Internal fiber failure, executor control fiber marked as free"); 236 | FOUNDATION_ASSERT_MSG(fiber->state == TASK_FIBER_FINISHED, 237 | "Internal fiber failure, finished fiber not in finished state"); 238 | fiber->state = TASK_FIBER_FREE; 239 | 240 | #if BUILD_ENABLE_ERROR_CONTEXT 241 | memory_deallocate(fiber->error_context); 242 | fiber->error_context = nullptr; 243 | #endif 244 | 245 | mutex_lock(executor->fiber_finished_lock); 246 | fiber->fiber_next = executor->fiber_finished; 247 | executor->fiber_finished = fiber; 248 | mutex_unlock(executor->fiber_finished_lock); 249 | } 250 | -------------------------------------------------------------------------------- /task/executor.h: -------------------------------------------------------------------------------- 1 | /* executor.h - Task library - Public Domain - 2013 Mattias Jansson 2 | * 3 | * This library provides a cross-platform library in C11 providing 4 | * task-based parallellism for projects based on our foundation library. 5 | * 6 | * The latest source code maintained by Mattias Jansson is always available at 7 | * 8 | * https://github.com/mjansson/task_lib 9 | * 10 | * The foundation library source code maintained by Mattias Jansson is always available at 11 | * 12 | * https://github.com/mjansson/foundation_lib 13 | * 14 | * This library is put in the public domain; you can redistribute it and/or modify it without any restrictions. 15 | * 16 | */ 17 | 18 | #pragma once 19 | 20 | /*! \file executor.h 21 | Task executor thread */ 22 | 23 | #include 24 | 25 | #include 26 | #include 27 | 28 | /*! Task executor thread entry point 29 | * \param arg Thread argument (executor pointer) 30 | * \return Result (0) 31 | */ 32 | TASK_API void* 33 | task_executor_thread(void* arg); 34 | 35 | /*! Notify executor that the fiber finished executing 36 | * \param executor Task executor 37 | * \param fiber Free fiber control structure */ 38 | TASK_API void 39 | task_executor_finished_fiber(task_executor_t* executor, task_fiber_t* fiber); 40 | -------------------------------------------------------------------------------- /task/fiber.c: -------------------------------------------------------------------------------- 1 | /* fiber.c - Task library - Public Domain - 2013 Mattias Jansson 2 | * 3 | * This library provides a cross-platform library in C11 providing 4 | * task-based parallellism for projects based on our foundation library. 5 | * 6 | * The latest source code maintained by Mattias Jansson is always available at 7 | * 8 | * https://github.com/mjansson/task_lib 9 | * 10 | * The foundation library source code maintained by Mattias Jansson is always available at 11 | * 12 | * https://github.com/mjansson/foundation_lib 13 | * 14 | * This library is put in the public domain; you can redistribute it and/or modify it without any restrictions. 15 | * 16 | */ 17 | 18 | #include "fiber.h" 19 | #include "executor.h" 20 | #include "scheduler.h" 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #if FOUNDATION_PLATFORM_APPLE 27 | #define _XOPEN_SOURCE 28 | #endif 29 | 30 | #include 31 | #include 32 | 33 | #if FOUNDATION_COMPILER_CLANG 34 | #pragma clang diagnostic push 35 | #if __has_warning("-Walloca") 36 | #pragma clang diagnostic ignored "-Walloca" 37 | #endif 38 | #endif 39 | 40 | #if FOUNDATION_PLATFORM_APPLE 41 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 42 | #endif 43 | #if FOUNDATION_PLATFORM_POSIX 44 | #include 45 | #endif 46 | 47 | extern task_executor_t* 48 | task_executor_thread_current(void); 49 | 50 | extern void 51 | task_executor_finished_fiber_internal(task_executor_t* executor, task_fiber_t* fiber); 52 | 53 | #if FOUNDATION_PLATFORM_WINDOWS 54 | //! Used for return address of executor control fiber context 55 | static void FOUNDATION_NOINLINE 56 | task_fiber_resume(void) { 57 | } 58 | #endif 59 | 60 | bool FOUNDATION_NOINLINE 61 | task_fiber_initialize_from_current_thread(task_fiber_t* fiber) { 62 | fiber->state = TASK_FIBER_THREAD; 63 | fiber->fiber_next = nullptr; 64 | #if FOUNDATION_PLATFORM_WINDOWS 65 | 66 | NT_TIB* tib = (NT_TIB*)NtCurrentTeb(); 67 | memcpy(fiber->tib, tib, sizeof(NT_TIB)); 68 | fiber->stack = (void*)tib->StackLimit; 69 | fiber->stack_size = pointer_diff((void*)tib->StackBase, fiber->stack); 70 | 71 | CONTEXT* context = fiber->context; 72 | context->ContextFlags = CONTEXT_FULL; 73 | BOOL res = GetThreadContext(GetCurrentThread(), context); 74 | if (!FOUNDATION_VALIDATE_MSG(res != 0, "Failed to get current thread context for fiber")) 75 | return false; 76 | // The stack pointer cannot be used as set by GetThreadContext, as it will be 77 | // captured inside the scope of the kernel DLL function. It will contain some other 78 | // data when actually executed. Capture the stack pointer as seen by this function 79 | // and simulate a immediate return by the dummy resume function (instruction pointer 80 | // will point to the ret instruction). It will pop the return value from the stack 81 | // which we have set to the address of the return address. 82 | context->Rsp = (DWORD64)_AddressOfReturnAddress(); 83 | context->Rbp = 0; 84 | context->Rip = (DWORD64)task_fiber_resume; 85 | #elif FOUNDATION_PLATFORM_POSIX 86 | ucontext_t* context = fiber->context; 87 | getcontext(context); 88 | #else 89 | #error Not implemented 90 | #endif 91 | return true; 92 | } 93 | 94 | #if FOUNDATION_PLATFORM_WINDOWS 95 | static FOUNDATION_NOINLINE void STDCALL 96 | task_fiber_trampoline(long rcx, long rdx, long r8, long r9, task_fiber_t* fiber) { 97 | FOUNDATION_UNUSED(rcx, rdx, r8, r9); 98 | #else 99 | static FOUNDATION_NOINLINE void STDCALL 100 | task_fiber_trampoline(int fiber_low, int fiber_high) { 101 | // Reconstruct 64bit pointer 102 | task_fiber_t* fiber = (void*)(((uintptr_t)((uint)fiber_high) << 32ULL) | (uintptr_t)((uint)fiber_low)); 103 | #endif 104 | FOUNDATION_ASSERT_MSG(fiber->state != TASK_FIBER_THREAD, 105 | "Internal fiber failure, executor control fiber used as task fiber"); 106 | FOUNDATION_ASSERT_MSG(fiber->state == TASK_FIBER_RUNNING, 107 | "Internal fiber failure, running fiber not in running state"); 108 | 109 | task_scheduler_t* scheduler = task_executor_thread_current()->scheduler; 110 | task_fiber_t* fiber_waiting = nullptr; 111 | 112 | // Mark a fiber that was pending finished as actually finished (see comment 113 | // below about current fiber when switching to a task with a pending fiber) 114 | if (fiber->fiber_pending_finished) { 115 | task_fiber_t* fiber_finished = fiber->fiber_pending_finished; 116 | fiber->fiber_pending_finished = nullptr; 117 | atomic_thread_fence_release(); 118 | task_executor_finished_fiber_internal(task_executor_thread_current(), fiber_finished); 119 | } 120 | 121 | FOUNDATION_ASSERT_MSG(fiber->state == TASK_FIBER_RUNNING, 122 | "Internal fiber failure, running fiber not in running state when calling task function"); 123 | 124 | atomic32_t* counter = fiber->task.counter; 125 | fiber->task.function(fiber->task.context); 126 | 127 | FOUNDATION_ASSERT_MSG(fiber->state == TASK_FIBER_RUNNING, 128 | "Internal fiber failure, running fiber not in running state after calling task function"); 129 | #if BUILD_ENABLE_ERROR_CONTEXT 130 | FOUNDATION_ASSERT_MSG(!fiber->error_context || !((error_context_t*)fiber->error_context)->depth, 131 | "Fiber finished with non-null error context"); 132 | error_context_t* current_error_context = error_context(); 133 | FOUNDATION_ASSERT_MSG(!current_error_context || !current_error_context->depth, 134 | "Fiber finished with non-zero error context depth"); 135 | #endif 136 | 137 | if (fiber->fiber_pending_finished) { 138 | task_fiber_t* fiber_finished = fiber->fiber_pending_finished; 139 | fiber->fiber_pending_finished = nullptr; 140 | atomic_thread_fence_release(); 141 | task_executor_finished_fiber_internal(task_executor_thread_current(), fiber_finished); 142 | } 143 | 144 | if (counter) { 145 | if (!atomic_decr32(counter, memory_order_relaxed)) { 146 | // Get the fiber waiting for this subtask counter completion 147 | fiber_waiting = task_scheduler_pop_fiber_waiting(scheduler, counter); 148 | } 149 | } 150 | 151 | while (atomic_load32(&scheduler->running, memory_order_relaxed)) { 152 | // Check if there is a previously waiting fiber that is ready to execute 153 | if (fiber_waiting) { 154 | // This fiber has now finished, but cannot be released until the new fiber is executing in 155 | // it's own stack or it could be prematurely reused 156 | FOUNDATION_ASSERT_MSG(!fiber_waiting->fiber_pending_finished, 157 | "Internal fiber failure, continuation fiber already has pending finished fiber"); 158 | FOUNDATION_ASSERT_MSG(!fiber->fiber_pending_finished, 159 | "Internal fiber failure, finished fiber has pending finished fiber"); 160 | FOUNDATION_ASSERT_MSG(fiber->state == TASK_FIBER_RUNNING, 161 | "Internal fiber failure, running fiber not in running state"); 162 | #if BUILD_ENABLE_ERROR_CONTEXT 163 | fiber->error_context = error_context_set(nullptr); 164 | #endif 165 | fiber->state = TASK_FIBER_FINISHED; 166 | fiber_waiting->fiber_pending_finished = fiber; 167 | 168 | FOUNDATION_ASSERT_MSG(fiber_waiting->state == TASK_FIBER_YIELD, 169 | "Internal fiber failure, waiting fiber not in yield state when resuming in fiber"); 170 | 171 | // Switch to the waiting task fiber to execute it 172 | task_fiber_switch(fiber->fiber_return, fiber_waiting); 173 | 174 | // We will never return here since the fiber switched to will 175 | // switch back to the return context immediately 176 | FOUNDATION_ASSERT_FAIL_LOG(HASH_TASK, "Internal fiber failure, control returned to unreachable code"); 177 | exception_raise_abort(); 178 | } 179 | 180 | // Optimization, check if we can reuse this fiber immediately without 181 | // switching context back to the executor task loop (tail recursion) 182 | if (task_scheduler_next_task(scheduler, &fiber->task)) { 183 | // This is a new task, reuse this fiber 184 | counter = fiber->task.counter; 185 | fiber->task.function(fiber->task.context); 186 | 187 | FOUNDATION_ASSERT_MSG( 188 | fiber->state == TASK_FIBER_RUNNING, 189 | "Internal fiber failure, running fiber not in running state after calling task function"); 190 | #if BUILD_ENABLE_ERROR_CONTEXT 191 | FOUNDATION_ASSERT_MSG(!fiber->error_context || !((error_context_t*)fiber->error_context)->depth, 192 | "Fiber finished with non-null error context"); 193 | current_error_context = error_context(); 194 | FOUNDATION_ASSERT_MSG(!current_error_context || !current_error_context->depth, 195 | "Fiber finished with non-zero error context depth"); 196 | #endif 197 | 198 | if (fiber->fiber_pending_finished) { 199 | task_fiber_t* fiber_finished = fiber->fiber_pending_finished; 200 | fiber->fiber_pending_finished = nullptr; 201 | atomic_thread_fence_release(); 202 | task_executor_finished_fiber_internal(task_executor_thread_current(), fiber_finished); 203 | } 204 | 205 | if (counter) { 206 | if (!atomic_decr32(counter, memory_order_relaxed)) { 207 | // Get the fiber waiting for this subtask counter completion 208 | fiber_waiting = task_scheduler_pop_fiber_waiting(scheduler, counter); 209 | } 210 | } 211 | } else { 212 | // Break and return to executor control fiber 213 | break; 214 | } 215 | } 216 | 217 | FOUNDATION_ASSERT_MSG(!fiber->fiber_return->fiber_pending_finished, 218 | "Internal fiber failure, return context already has pending finished fiber"); 219 | fiber->fiber_return->fiber_pending_finished = fiber; 220 | 221 | FOUNDATION_ASSERT_MSG(fiber->fiber_return->state == TASK_FIBER_EXECUTOR, 222 | "Internal fiber failure, return to executor fiber not in executor state"); 223 | FOUNDATION_ASSERT_MSG(fiber->state == TASK_FIBER_RUNNING, 224 | "Internal fiber failure, running fiber not in running state"); 225 | #if BUILD_ENABLE_ERROR_CONTEXT 226 | FOUNDATION_ASSERT_MSG(!fiber->error_context || !((error_context_t*)fiber->error_context)->depth, 227 | "Fiber finished with non-null error context"); 228 | fiber->error_context = error_context_set(nullptr); 229 | #endif 230 | fiber->state = TASK_FIBER_FINISHED; 231 | 232 | task_fiber_switch(nullptr, fiber->fiber_return); 233 | } 234 | 235 | bool FOUNDATION_NOINLINE 236 | task_fiber_initialize(task_fiber_t* fiber) { 237 | #if FOUNDATION_PLATFORM_WINDOWS 238 | NT_TIB* tib = (NT_TIB*)NtCurrentTeb(); 239 | memcpy(fiber->tib, tib, sizeof(NT_TIB)); 240 | NT_TIB* fiber_tib = fiber->tib; 241 | // fiber_tib->FiberData = fiber; 242 | fiber_tib->StackBase = fiber->stack; 243 | fiber_tib->StackLimit = pointer_offset(fiber->stack, -(ssize_t)fiber->stack_size); 244 | 245 | CONTEXT* context = fiber->context; 246 | context->ContextFlags = CONTEXT_FULL; 247 | BOOL res = GetThreadContext(GetCurrentThread(), context); 248 | if (!FOUNDATION_VALIDATE_MSG(res != 0, "Failed to get current thread context for fiber")) 249 | return false; 250 | 251 | FOUNDATION_ASSERT_MSG(fiber->state != TASK_FIBER_THREAD, 252 | "Internal fiber failure, executor control fiber used for execution of task"); 253 | FOUNDATION_ASSERT_MSG(fiber->stack, "Internal fiber failure, fiber without a stack used"); 254 | 255 | void* stack_pointer = fiber->stack; 256 | void** argument_pointer = (void**)pointer_offset(stack_pointer, -32); 257 | *(argument_pointer + 0) = fiber; 258 | *(argument_pointer + 1) = 0; 259 | *(argument_pointer + 2) = 0; 260 | *(argument_pointer + 3) = 0; 261 | stack_pointer = pointer_offset(argument_pointer, -40); 262 | void** stack_content = (void**)stack_pointer; 263 | *(stack_content + 0) = 0; 264 | *(stack_content + 1) = 0; 265 | *(stack_content + 2) = 0; 266 | *(stack_content + 3) = 0; 267 | *(stack_content + 4) = 0; 268 | context->Rsp = (DWORD64)stack_pointer; 269 | context->Rbp = 0; 270 | context->Rip = (DWORD64)task_fiber_trampoline; 271 | context->ContextFlags = CONTEXT_FULL; 272 | #elif FOUNDATION_PLATFORM_POSIX 273 | ucontext_t* context = fiber->context; 274 | #if FOUNDATION_PLATFORM_APPLE 275 | memset(fiber->context, 0, sizeof(ucontext_t)); 276 | context->uc_mcontext = fiber->tib; 277 | context->uc_mcsize = sizeof(*context->uc_mcontext); 278 | #else 279 | if (fiber->state == TASK_FIBER_NOT_INITIALIZED) { 280 | memset(fiber->context, 0, sizeof(ucontext_t)); 281 | getcontext(context); 282 | } 283 | #endif 284 | context->uc_stack.ss_sp = pointer_offset(fiber->stack, -(ssize_t)fiber->stack_size); 285 | context->uc_stack.ss_size = fiber->stack_size; 286 | 287 | // Deconstruct 64bit pointer 288 | int fiber_low = (int)((uintptr_t)fiber & 0xFFFFFFFFULL); 289 | int fiber_high = (int)((uintptr_t)fiber >> 32ULL); 290 | 291 | makecontext(context, (void (*)(void))task_fiber_trampoline, 2, fiber_low, fiber_high); 292 | #else 293 | #error Not implemented 294 | #endif 295 | return true; 296 | } 297 | 298 | #if FOUNDATION_PLATFORM_WINDOWS 299 | extern void 300 | task_fiber_initialize_for_executor_thread(task_executor_t* executor, task_fiber_t* fiber, 301 | void (*executor_function)(long, long, long, long, task_executor_t*, 302 | task_fiber_t*)); 303 | 304 | void 305 | task_fiber_initialize_for_executor_thread(task_executor_t* executor, task_fiber_t* fiber, 306 | void (*executor_function)(long, long, long, long, task_executor_t*, 307 | task_fiber_t*)) { 308 | #elif FOUNDATION_PLATFORM_POSIX 309 | extern void 310 | task_fiber_initialize_for_executor_thread(task_executor_t* executor, task_fiber_t* fiber, 311 | void (*executor_function)(int, int, int, int, int, int, int, int, int, int)); 312 | 313 | void 314 | task_fiber_initialize_for_executor_thread(task_executor_t* executor, task_fiber_t* fiber, 315 | void (*executor_function)(int, int, int, int, int, int, int, int, int, int)) { 316 | #endif 317 | #if FOUNDATION_PLATFORM_POSIX 318 | bool was_initialized = (fiber->state != TASK_FIBER_NOT_INITIALIZED); 319 | #endif 320 | fiber->state = TASK_FIBER_EXECUTOR; 321 | fiber->fiber_next = nullptr; 322 | 323 | #if FOUNDATION_PLATFORM_WINDOWS 324 | NT_TIB* tib = (NT_TIB*)NtCurrentTeb(); 325 | memcpy(fiber->tib, tib, sizeof(NT_TIB)); 326 | NT_TIB* fiber_tib = fiber->tib; 327 | // fiber_tib->FiberData = fiber; 328 | fiber_tib->StackBase = fiber->stack; 329 | fiber_tib->StackLimit = pointer_offset(fiber->stack, -(ssize_t)fiber->stack_size); 330 | 331 | CONTEXT* context = fiber->context; 332 | context->ContextFlags = CONTEXT_FULL; 333 | BOOL res = GetThreadContext(GetCurrentThread(), context); 334 | if (!FOUNDATION_VALIDATE_MSG(res != 0, "Failed to get current thread context for fiber")) 335 | return; 336 | 337 | FOUNDATION_ASSERT_MSG(fiber->state != TASK_FIBER_THREAD, 338 | "Internal fiber failure, executor control fiber used for execution of task"); 339 | FOUNDATION_ASSERT_MSG(fiber->stack, "Internal fiber failure, fiber without a stack used"); 340 | 341 | void* stack_pointer = fiber->stack; 342 | void** argument_pointer = (void**)pointer_offset(stack_pointer, -32); 343 | *(argument_pointer + 0) = executor; 344 | *(argument_pointer + 1) = fiber; 345 | *(argument_pointer + 2) = 0; 346 | *(argument_pointer + 3) = 0; 347 | stack_pointer = pointer_offset(argument_pointer, -40); 348 | void** stack_content = (void**)stack_pointer; 349 | *(stack_content + 0) = 0; 350 | *(stack_content + 1) = 0; 351 | *(stack_content + 2) = 0; 352 | *(stack_content + 3) = 0; 353 | *(stack_content + 4) = 0; 354 | context->Rsp = (DWORD64)stack_pointer; 355 | context->Rbp = 0; 356 | context->Rip = (DWORD64)executor_function; 357 | context->ContextFlags = CONTEXT_FULL; 358 | #elif FOUNDATION_PLATFORM_POSIX 359 | ucontext_t* context = fiber->context; 360 | FOUNDATION_UNUSED(was_initialized); 361 | #if FOUNDATION_PLATFORM_APPLE 362 | memset(fiber->context, 0, sizeof(ucontext_t)); 363 | context->uc_mcontext = fiber->tib; 364 | context->uc_mcsize = sizeof(*context->uc_mcontext); 365 | #else 366 | if (!was_initialized) { 367 | memset(fiber->context, 0, sizeof(ucontext_t)); 368 | getcontext(context); 369 | } 370 | #endif 371 | context->uc_stack.ss_sp = pointer_offset(fiber->stack, -(ssize_t)fiber->stack_size); 372 | context->uc_stack.ss_size = fiber->stack_size; 373 | 374 | // Deconstruct 64bit pointers 375 | int executor_low = (int)((uintptr_t)executor & 0xFFFFFFFFULL); 376 | int executor_high = (int)((uintptr_t)executor >> 32ULL); 377 | int fiber_low = (int)((uintptr_t)fiber & 0xFFFFFFFFULL); 378 | int fiber_high = (int)((uintptr_t)fiber >> 32ULL); 379 | 380 | makecontext(context, (void (*)(void))executor_function, 10, 0, 1, 2, 3, 4, 5, executor_low, executor_high, 381 | fiber_low, fiber_high); 382 | #else 383 | #error Not implemented 384 | #endif 385 | } 386 | 387 | FOUNDATION_NOINLINE void 388 | task_fiber_switch(task_fiber_t* from, task_fiber_t* to) { 389 | FOUNDATION_ASSERT(to != nullptr); 390 | FOUNDATION_ASSERT(!to->fiber_next); 391 | if (from) 392 | to->fiber_return = from; 393 | 394 | task_executor_thread_current()->fiber_current = to; 395 | 396 | #if BUILD_ENABLE_ERROR_CONTEXT 397 | error_context_set(to->error_context); 398 | #endif 399 | 400 | #if FOUNDATION_PLATFORM_WINDOWS 401 | BOOL res; 402 | HANDLE thread = GetCurrentThread(); 403 | CONTEXT* to_context = to->context; 404 | 405 | // Copy stack pointers to new thread information block 406 | NT_TIB* thread_tib = (NT_TIB*)NtCurrentTeb(); 407 | NT_TIB* fiber_tib = (NT_TIB*)to->tib; 408 | thread_tib->StackBase = fiber_tib->StackBase; 409 | thread_tib->StackLimit = fiber_tib->StackLimit; 410 | 411 | // Switch to fiber context 412 | res = SetThreadContext(thread, to_context); 413 | if (!FOUNDATION_VALIDATE_MSG(res != 0, "Failed to switch current fiber context")) { 414 | exception_raise_abort(); 415 | } 416 | #elif FOUNDATION_PLATFORM_POSIX 417 | int res = setcontext(to->context); 418 | if (!FOUNDATION_VALIDATE_MSG(res == 0, "Failed to switch current fiber context")) { 419 | exception_raise_abort(); 420 | } 421 | #else 422 | #error Not implemented 423 | #endif 424 | } 425 | 426 | static FOUNDATION_NOINLINE void 427 | task_fiber_push_waiting_and_yield(volatile void* stack_reserve, task_fiber_t* fiber, atomic32_t* counter) { 428 | FOUNDATION_UNUSED(stack_reserve); 429 | task_scheduler_push_fiber_waiting_and_yield(task_executor_thread_current()->scheduler, fiber, counter); 430 | } 431 | 432 | FOUNDATION_NOINLINE void 433 | task_fiber_yield(task_fiber_t* fiber, atomic32_t* counter) { 434 | FOUNDATION_ASSERT_MSG(fiber->state == TASK_FIBER_RUNNING, "Yielding a non-running fiber is not allowed"); 435 | if (fiber->state != TASK_FIBER_RUNNING) 436 | return; 437 | #if BUILD_ENABLE_ERROR_CONTEXT 438 | fiber->error_context = error_context_set(nullptr); 439 | #endif 440 | #if FOUNDATION_PLATFORM_WINDOWS 441 | HANDLE thread = GetCurrentThread(); 442 | CONTEXT* context = fiber->context; 443 | BOOL res = GetThreadContext(thread, context); 444 | if (!FOUNDATION_VALIDATE_MSG(res != 0, "Failed to store current fiber context in fiber yield")) { 445 | exception_raise_abort(); 446 | return; 447 | } 448 | #elif FOUNDATION_PLATFORM_POSIX 449 | getcontext(fiber->context); 450 | #else 451 | #error Not implemented 452 | #endif 453 | if (fiber->state == TASK_FIBER_RUNNING) { 454 | atomic_thread_fence_release(); 455 | volatile void* stack_reserve = alloca(256); 456 | task_fiber_push_waiting_and_yield(stack_reserve, fiber, counter); 457 | } 458 | if (fiber->state == TASK_FIBER_YIELD) { 459 | fiber->state = TASK_FIBER_RUNNING; 460 | } 461 | } 462 | -------------------------------------------------------------------------------- /task/fiber.h: -------------------------------------------------------------------------------- 1 | /* fiber.h - Task library - Public Domain - 2013 Mattias Jansson 2 | * 3 | * This library provides a cross-platform library in C11 providing 4 | * task-based parallellism for projects based on our foundation library. 5 | * 6 | * The latest source code maintained by Mattias Jansson is always available at 7 | * 8 | * https://github.com/mjansson/task_lib 9 | * 10 | * The foundation library source code maintained by Mattias Jansson is always available at 11 | * 12 | * https://github.com/mjansson/foundation_lib 13 | * 14 | * This library is put in the public domain; you can redistribute it and/or modify it without any restrictions. 15 | * 16 | */ 17 | 18 | #pragma once 19 | 20 | /*! \file fiber.h 21 | Task fiber abstraction */ 22 | 23 | #include 24 | 25 | #include 26 | #include 27 | 28 | /*! Initialize a fiber for calling a 29 | * \param fiber Fiber control structure 30 | * \return true if success, false if error 31 | */ 32 | TASK_API bool 33 | task_fiber_initialize_from_current_thread(task_fiber_t* fiber); 34 | 35 | /*! Initialize a fiber for executing a task 36 | * \param fiber Fiber control structure 37 | */ 38 | TASK_API bool 39 | task_fiber_initialize(task_fiber_t* fiber); 40 | 41 | /*! Switch fiber 42 | * \param from Fiber to switch from 43 | * \param to Fiber to switch to 44 | */ 45 | TASK_API void 46 | task_fiber_switch(task_fiber_t* from, task_fiber_t* to); 47 | 48 | /*! Yield fiber 49 | * \param fiber Fiber to yield 50 | * \param counter Counter to wait on 51 | */ 52 | TASK_API void 53 | task_fiber_yield(task_fiber_t* fiber, atomic32_t* counter); 54 | 55 | /*! Get the current fiber executing in this thread 56 | * \return Fiber executing in this thread, null if none */ 57 | TASK_API task_fiber_t* 58 | task_fiber_current(void); 59 | -------------------------------------------------------------------------------- /task/hashstrings.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | /* ****** AUTOMATICALLY GENERATED, DO NOT EDIT ****** 6 | Edit corresponding definitions file and rerun 7 | the foundation hashify tool to update this file */ 8 | 9 | #define HASH_TASK static_hash_string("task", 4, 0x1172f53a50ad4fc0ULL) 10 | -------------------------------------------------------------------------------- /task/hashstrings.txt: -------------------------------------------------------------------------------- 1 | 2 | HASH_TASK task 3 | -------------------------------------------------------------------------------- /task/scheduler.h: -------------------------------------------------------------------------------- 1 | /* scheduler.h - Task library - Public Domain - 2013 Mattias Jansson 2 | * 3 | * This library provides a cross-platform library in C11 providing 4 | * task-based parallellism for projects based on our foundation library. 5 | * 6 | * The latest source code maintained by Mattias Jansson is always available at 7 | * 8 | * https://github.com/mjansson/task_lib 9 | * 10 | * The foundation library source code maintained by Mattias Jansson is always available at 11 | * 12 | * https://github.com/mjansson/foundation_lib 13 | * 14 | * This library is put in the public domain; you can redistribute it and/or modify it without any restrictions. 15 | * 16 | */ 17 | 18 | #pragma once 19 | 20 | /*! \file scheduler.h 21 | Task scheduler */ 22 | 23 | #include 24 | 25 | #include 26 | #include 27 | 28 | /*! Allocate a scheduler 29 | \param executor_count Number of executor threads 30 | \param fiber_count Number of fibers 31 | \return New task scheduler */ 32 | TASK_API task_scheduler_t* 33 | task_scheduler_allocate(size_t executor_count, size_t fiber_count); 34 | 35 | /*! Deallocate a scheduler 36 | \param scheduler Task scheduler */ 37 | TASK_API void 38 | task_scheduler_deallocate(task_scheduler_t* scheduler); 39 | 40 | /*! Queue a task 41 | \param scheduler Task scheduler 42 | \param task Task */ 43 | TASK_API void 44 | task_scheduler_queue(task_scheduler_t* scheduler, task_t task); 45 | 46 | /*! Queue multiple tasks 47 | \param scheduler Task scheduler 48 | \param task Tasks 49 | \param task_count Number of tasks */ 50 | TASK_API void 51 | task_scheduler_multiqueue(task_scheduler_t* scheduler, task_t* task, size_t task_count); 52 | 53 | /*! Pop the next task from the scheduler queue 54 | * \param scheduler Task scheduler 55 | * \param task Task structure to fill */ 56 | TASK_API bool 57 | task_scheduler_next_task(task_scheduler_t* scheduler, task_t* task); 58 | 59 | /*! Pop a free fiber from the scheduler pool 60 | * \param scheduler Task scheduler 61 | * \return Free fiber control structure */ 62 | TASK_API task_fiber_t* 63 | task_scheduler_next_free_fiber(task_scheduler_t* scheduler); 64 | 65 | /*! Add fiber as waiting on subtask counter 66 | * \param scheduler Scheduler 67 | * \param fiber Fiber 68 | * \param counter Subtask counter 69 | * \return true if fiber is ready to execute, false if waiting on subtask counter */ 70 | TASK_API bool 71 | task_scheduler_push_fiber_waiting_and_yield(task_scheduler_t* scheduler, task_fiber_t* fiber, atomic32_t* counter); 72 | 73 | /*! Get fiber that was waiting for the given counter 74 | * \param scheduler Scheduler 75 | * \param counter Subtask counter 76 | * \return Fiber that was waiting for the given subtask counter */ 77 | TASK_API task_fiber_t* 78 | task_scheduler_pop_fiber_waiting(task_scheduler_t* scheduler, atomic32_t* counter); 79 | -------------------------------------------------------------------------------- /task/task.c: -------------------------------------------------------------------------------- 1 | /* task.c - Task library - Public Domain - 2013 Mattias Jansson 2 | * 3 | * This library provides a cross-platform library in C11 providing 4 | * task-based parallellism for projects based on our foundation library. 5 | * 6 | * The latest source code maintained by Mattias Jansson is always available at 7 | * 8 | * https://github.com/mjansson/task_lib 9 | * 10 | * The foundation library source code maintained by Mattias Jansson is always available at 11 | * 12 | * https://github.com/mjansson/foundation_lib 13 | * 14 | * This library is put in the public domain; you can redistribute it and/or modify it without any restrictions. 15 | * 16 | */ 17 | 18 | #include "task.h" 19 | 20 | #include 21 | 22 | #if FOUNDATION_PLATFORM_POSIX 23 | #include 24 | #endif 25 | 26 | static task_config_t task_config; 27 | static bool task_initialized; 28 | 29 | static void 30 | task_module_initialize_config(const task_config_t config) { 31 | task_config = config; 32 | 33 | #if FOUNDATION_PLATFORM_POSIX 34 | size_t min_stack_size = (size_t)MINSIGSTKSZ; 35 | #if BUILD_DEBUG 36 | size_t default_stack_size = 128 * 1024; 37 | #else 38 | size_t default_stack_size = 64 * 1024; 39 | #endif 40 | size_t max_stack_size = 2 * 1024 * 1024; 41 | #else 42 | size_t min_stack_size = 8 * 1024; 43 | size_t default_stack_size = 64 * 1024; 44 | size_t max_stack_size = 2 * 1024 * 1024; 45 | #endif 46 | 47 | if (!task_config.fiber_stack_size) 48 | task_config.fiber_stack_size = default_stack_size; 49 | else if (task_config.fiber_stack_size < min_stack_size) 50 | task_config.fiber_stack_size = min_stack_size; 51 | else if (task_config.fiber_stack_size > max_stack_size) 52 | task_config.fiber_stack_size = max_stack_size; 53 | } 54 | 55 | int 56 | task_module_initialize(const task_config_t config) { 57 | if (task_initialized) 58 | return 0; 59 | 60 | task_module_initialize_config(config); 61 | 62 | task_initialized = true; 63 | 64 | return 0; 65 | } 66 | 67 | bool 68 | task_module_is_initialized(void) { 69 | return task_initialized; 70 | } 71 | 72 | task_config_t 73 | task_module_config(void) { 74 | return task_config; 75 | } 76 | 77 | void 78 | task_module_finalize(void) { 79 | task_initialized = false; 80 | } 81 | 82 | extern task_executor_t* 83 | task_executor_thread_current(void); 84 | 85 | FOUNDATION_NOINLINE void 86 | task_yield_and_wait(atomic32_t* counter) { 87 | if (!counter) 88 | return; 89 | task_executor_t* executor = task_executor_thread_current(); 90 | if (executor) { 91 | if (atomic_incr32(counter, memory_order_relaxed) > 1) 92 | task_fiber_yield(executor->fiber_current, counter); 93 | else 94 | atomic_decr32(counter, memory_order_relaxed); 95 | } else { 96 | // TODO: Do a task executor step instead of yielding thread slice 97 | while (atomic_load32(counter, memory_order_relaxed)) 98 | thread_yield(); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /task/task.h: -------------------------------------------------------------------------------- 1 | /* task.h - Task library - Public Domain - 2013 Mattias Jansson 2 | * 3 | * This library provides a cross-platform library in C11 providing 4 | * task-based parallellism for projects based on our foundation library. 5 | * 6 | * The latest source code maintained by Mattias Jansson is always available at 7 | * 8 | * https://github.com/mjansson/task_lib 9 | * 10 | * The foundation library source code maintained by Mattias Jansson is always available at 11 | * 12 | * https://github.com/mjansson/foundation_lib 13 | * 14 | * This library is put in the public domain; you can redistribute it and/or modify it without any restrictions. 15 | * 16 | */ 17 | 18 | #pragma once 19 | 20 | /*! \file task.h 21 | Task library entry points */ 22 | 23 | #include 24 | 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | /*! Initialize task library 33 | \param config Task library configuration 34 | \return 0 if success, <0 if error */ 35 | TASK_API int 36 | task_module_initialize(const task_config_t config); 37 | 38 | /*! Finalize task library */ 39 | TASK_API void 40 | task_module_finalize(void); 41 | 42 | /*! Query if task library is initialized 43 | \return true if initialized, false if not */ 44 | TASK_API bool 45 | task_module_is_initialized(void); 46 | 47 | /*! Get the task library config 48 | \return Current configuration */ 49 | TASK_API task_config_t 50 | task_module_config(void); 51 | 52 | /* Get task library version 53 | \return Task library version */ 54 | TASK_API version_t 55 | task_module_version(void); 56 | 57 | /*! Main task synchronization point, yield execution and wait for subtasks to complete before continuing 58 | * \param counter Subtask counter to wait on */ 59 | TASK_API FOUNDATION_NOINLINE void 60 | task_yield_and_wait(atomic32_t* counter); 61 | -------------------------------------------------------------------------------- /task/types.h: -------------------------------------------------------------------------------- 1 | /* types.h - Task library - Public Domain - 2013 Mattias Jansson 2 | * 3 | * This library provides a cross-platform library in C11 providing 4 | * task-based parallellism for projects based on our foundation library. 5 | * 6 | * The latest source code maintained by Mattias Jansson is always available at 7 | * 8 | * https://github.com/mjansson/task_lib 9 | * 10 | * The foundation library source code maintained by Mattias Jansson is always available at 11 | * 12 | * https://github.com/mjansson/foundation_lib 13 | * 14 | * This library is put in the public domain; you can redistribute it and/or modify it without any 15 | * restrictions. 16 | * 17 | */ 18 | 19 | #pragma once 20 | 21 | /*! \file types.h 22 | Task data types */ 23 | 24 | #include 25 | #include 26 | 27 | #include 28 | 29 | #if defined(TASK_COMPILE) && TASK_COMPILE 30 | #ifdef __cplusplus 31 | #define TASK_EXTERN extern "C" 32 | #define TASK_API extern "C" 33 | #else 34 | #define TASK_EXTERN extern 35 | #define TASK_API extern 36 | #endif 37 | #else 38 | #ifdef __cplusplus 39 | #define TASK_EXTERN extern "C" 40 | #define TASK_API extern "C" 41 | #else 42 | #define TASK_EXTERN extern 43 | #define TASK_API extern 44 | #endif 45 | #endif 46 | 47 | /*! Task library configuration */ 48 | typedef struct task_config_t task_config_t; 49 | /*! Task declaration */ 50 | typedef struct task_t task_t; 51 | /*! Task scheduler control */ 52 | typedef struct task_scheduler_t task_scheduler_t; 53 | /*! Task executor thread control */ 54 | typedef struct task_executor_t task_executor_t; 55 | /*! Task fiber control */ 56 | typedef struct task_fiber_t task_fiber_t; 57 | /*! Block of tasks in a queue */ 58 | typedef struct task_queue_block_t task_queue_block_t; 59 | 60 | /*! Context type for a task */ 61 | typedef void* task_context_t; 62 | /*! Counter for task dependencies and synchronization */ 63 | typedef atomic32_t task_counter_t; 64 | 65 | /*! Task function */ 66 | typedef void (*task_fn)(task_context_t context); 67 | 68 | /*! Number of tasks in one queue block */ 69 | #define TASK_QUEUE_BLOCK_CAPACITY 256 70 | 71 | typedef enum task_fiber_state { 72 | TASK_FIBER_NOT_INITIALIZED = 0, 73 | TASK_FIBER_THREAD, 74 | TASK_FIBER_EXECUTOR, 75 | TASK_FIBER_FREE, 76 | TASK_FIBER_RUNNING, 77 | TASK_FIBER_YIELD, 78 | TASK_FIBER_FINISHED 79 | } task_fiber_state; 80 | 81 | /*! Task declaration */ 82 | struct task_t { 83 | /*! Function to execute */ 84 | task_fn function; 85 | /*! Task context */ 86 | task_context_t context; 87 | /*! Task counter */ 88 | atomic32_t* counter; 89 | }; 90 | 91 | /*! Block of tasks in a queue */ 92 | struct task_queue_block_t { 93 | /*! Tasks */ 94 | task_t task[TASK_QUEUE_BLOCK_CAPACITY]; 95 | /*! Read offset */ 96 | size_t read; 97 | /*! Write offset */ 98 | size_t write; 99 | /*! Next task block */ 100 | task_queue_block_t* block_next; 101 | }; 102 | 103 | /*! Task executor control */ 104 | struct task_executor_t { 105 | /*! Owning task scheduler */ 106 | task_scheduler_t* scheduler; 107 | /*! Index of executor in scheduler */ 108 | size_t index; 109 | /*! Execution thread */ 110 | thread_t thread; 111 | /*! Thread fiber */ 112 | task_fiber_t* self_fiber; 113 | /*! Currently executing fiber */ 114 | task_fiber_t* fiber_current; 115 | /*! Free fiber (local to executor, cannot be accessed outside executor context) */ 116 | task_fiber_t* fiber_free; 117 | /*! Fiber that was just put in waiting hold (local to executor, cannot be accessed outside executor context) */ 118 | task_fiber_t* fiber_waiting_release; 119 | /*! First finished fiber index (linked list) */ 120 | task_fiber_t* fiber_finished; 121 | /*! List mutex */ 122 | mutex_t* fiber_finished_lock; 123 | }; 124 | 125 | /*! Task fiber control */ 126 | struct task_fiber_t { 127 | /*! Context */ 128 | void* context; 129 | /*! Thread information block */ 130 | void* tib; 131 | /*! Stack pointer */ 132 | void* stack; 133 | /*! Stack size */ 134 | size_t stack_size; 135 | /*! Index in scheduler fiber array */ 136 | uint index; 137 | /*! State */ 138 | task_fiber_state state; 139 | /*! Task */ 140 | task_t task; 141 | /*! Counter we are waiting on */ 142 | atomic32_t* waiting_counter; 143 | /*! Fiber to return to after execution finishes */ 144 | task_fiber_t* fiber_return; 145 | /*! Old fiber that should be released */ 146 | task_fiber_t* fiber_pending_finished; 147 | /*! Next fiber in a linked list */ 148 | task_fiber_t* fiber_next; 149 | /*! Error context */ 150 | void* error_context; 151 | /*! Platform data */ 152 | char platform_data[FOUNDATION_FLEXIBLE_ARRAY]; 153 | }; 154 | 155 | /*! Task scheduler control */ 156 | struct task_scheduler_t { 157 | /*! Total size of memory block */ 158 | size_t control_block_size; 159 | /*! Executors */ 160 | task_executor_t* executor; 161 | /*! Number of executors */ 162 | size_t executor_count; 163 | /*! Fibers */ 164 | task_fiber_t** fiber; 165 | /*! Number of fibers */ 166 | size_t fiber_count; 167 | /*! Total size of a fiber control block */ 168 | size_t fiber_size; 169 | /*! Size of fiber context */ 170 | size_t fiber_context_size; 171 | /*! Size of fiber tib */ 172 | size_t fiber_tib_size; 173 | /*! Wakeup signal */ 174 | semaphore_t signal; 175 | /*! Running flag */ 176 | atomic32_t running; 177 | /*! Free fibers */ 178 | task_fiber_t* fiber_free; 179 | /*! Current task block */ 180 | task_queue_block_t* task_queue_block; 181 | /*! Last queued task block */ 182 | task_queue_block_t* task_queue_block_tail; 183 | /*! Free task blocks */ 184 | task_queue_block_t* task_free_block; 185 | /*! Waiting tasks/fibers */ 186 | hashmap_t* fiber_waiting; 187 | /*! Lock for task blocks */ 188 | mutex_t* task_lock; 189 | /*! Lock for free fibers */ 190 | mutex_t* fiber_lock; 191 | /*! Lock for waiting fibers */ 192 | mutex_t* waiting_lock; 193 | /* Additional fiber blocks */ 194 | task_fiber_t** additional_fiber; 195 | }; 196 | 197 | /*! Task library config */ 198 | struct task_config_t { 199 | /*! Fiber stack size */ 200 | size_t fiber_stack_size; 201 | }; 202 | -------------------------------------------------------------------------------- /task/version.c: -------------------------------------------------------------------------------- 1 | /* ****** AUTOMATICALLY GENERATED, DO NOT EDIT ****** 2 | This file is generated from the git describe command. 3 | Run the configure script to regenerate this file */ 4 | 5 | #include 6 | #include 7 | 8 | version_t 9 | task_module_version(void) { 10 | return version_make(0, 0, 1, 0, 0x0); 11 | } 12 | -------------------------------------------------------------------------------- /test/all/android/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 19 | 20 | 21 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /test/all/android/drawable-hdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/task_lib/4305e770d295ea96b79c9be0d6921cbc089f9630/test/all/android/drawable-hdpi/icon.png -------------------------------------------------------------------------------- /test/all/android/drawable-ldpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/task_lib/4305e770d295ea96b79c9be0d6921cbc089f9630/test/all/android/drawable-ldpi/icon.png -------------------------------------------------------------------------------- /test/all/android/drawable-mdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/task_lib/4305e770d295ea96b79c9be0d6921cbc089f9630/test/all/android/drawable-mdpi/icon.png -------------------------------------------------------------------------------- /test/all/android/drawable-xhdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/task_lib/4305e770d295ea96b79c9be0d6921cbc089f9630/test/all/android/drawable-xhdpi/icon.png -------------------------------------------------------------------------------- /test/all/android/drawable-xxhdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/task_lib/4305e770d295ea96b79c9be0d6921cbc089f9630/test/all/android/drawable-xxhdpi/icon.png -------------------------------------------------------------------------------- /test/all/android/drawable-xxxhdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/task_lib/4305e770d295ea96b79c9be0d6921cbc089f9630/test/all/android/drawable-xxxhdpi/icon.png -------------------------------------------------------------------------------- /test/all/android/java/com/maniccoder/task/test/TestActivity.java: -------------------------------------------------------------------------------- 1 | package com.maniccoder.task.test; 2 | 3 | import android.os.Bundle; 4 | import android.app.NativeActivity; 5 | import android.graphics.Color; 6 | import android.graphics.Point; 7 | import android.widget.TextView; 8 | import android.util.Log; 9 | import android.widget.LinearLayout; 10 | import android.widget.PopupWindow; 11 | import android.view.Gravity; 12 | import android.view.Display; 13 | import android.view.ViewGroup; 14 | import android.view.ViewGroup.LayoutParams; 15 | import android.view.ViewGroup.MarginLayoutParams; 16 | 17 | public class TestActivity extends NativeActivity 18 | { 19 | private TextView textView; 20 | private boolean displayedTextView = false; 21 | 22 | @Override 23 | public void onWindowFocusChanged( boolean hasFocus ) 24 | { 25 | super.onWindowFocusChanged( hasFocus ); 26 | 27 | if( !displayedTextView && hasFocus ) 28 | { 29 | displayedTextView = true; 30 | 31 | setContentView( R.layout.main ); 32 | 33 | textView = (TextView)findViewById( R.id.logtext ); 34 | textView.setText( "" ); 35 | ((ViewGroup)textView.getParent()).removeView(textView); 36 | 37 | final TestActivity activity = this; 38 | 39 | runOnUiThread( new Runnable() { 40 | 41 | @Override 42 | public void run() 43 | { 44 | PopupWindow popup = new PopupWindow( activity ); 45 | 46 | Display display = getWindowManager().getDefaultDisplay(); 47 | Point size = new Point(); 48 | display.getSize( size ); 49 | 50 | popup.setWidth( size.x ); 51 | popup.setHeight( size.y ); 52 | popup.setWindowLayoutMode( LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT ); 53 | popup.setClippingEnabled( false ); 54 | 55 | MarginLayoutParams params = new MarginLayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT ); 56 | params.setMargins(0, 0, 0, 0); 57 | 58 | LinearLayout layout = new LinearLayout( activity ); 59 | layout.setOrientation( LinearLayout.VERTICAL ); 60 | layout.addView( activity.textView, params ); 61 | 62 | popup.setContentView( layout ); 63 | 64 | final ViewGroup viewGroup = (ViewGroup)((ViewGroup)activity.findViewById( android.R.id.content )).getChildAt(0); 65 | 66 | popup.showAtLocation( viewGroup, Gravity.TOP, 0, 0 ); 67 | popup.update(); 68 | } 69 | } ); 70 | } 71 | } 72 | 73 | public void appendLog( final String msg ) 74 | { 75 | runOnUiThread( new Runnable() { 76 | @Override 77 | public void run() 78 | { 79 | if( textView != null ) 80 | textView.append( msg ); 81 | } 82 | } ); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /test/all/android/layout/main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /test/all/android/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Task Test Suite 4 | 5 | -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "29x29", 5 | "idiom" : "iphone", 6 | "filename" : "icon_29.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "29x29", 11 | "idiom" : "iphone", 12 | "filename" : "icon_58.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "40x40", 17 | "idiom" : "iphone", 18 | "filename" : "icon_80-1.png", 19 | "scale" : "2x" 20 | }, 21 | { 22 | "size" : "57x57", 23 | "idiom" : "iphone", 24 | "filename" : "icon_57.png", 25 | "scale" : "1x" 26 | }, 27 | { 28 | "size" : "57x57", 29 | "idiom" : "iphone", 30 | "filename" : "icon_114.png", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "size" : "60x60", 35 | "idiom" : "iphone", 36 | "filename" : "icon_120.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "60x60", 41 | "idiom" : "iphone", 42 | "filename" : "icon_180.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "29x29", 47 | "idiom" : "ipad", 48 | "filename" : "icon_29-1.png", 49 | "scale" : "1x" 50 | }, 51 | { 52 | "size" : "29x29", 53 | "idiom" : "ipad", 54 | "filename" : "icon_58-1.png", 55 | "scale" : "2x" 56 | }, 57 | { 58 | "size" : "40x40", 59 | "idiom" : "ipad", 60 | "filename" : "icon_40.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "40x40", 65 | "idiom" : "ipad", 66 | "filename" : "icon_80.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "50x50", 71 | "idiom" : "ipad", 72 | "filename" : "icon_50.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "50x50", 77 | "idiom" : "ipad", 78 | "filename" : "icon_100.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "72x72", 83 | "idiom" : "ipad", 84 | "filename" : "icon_72.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "72x72", 89 | "idiom" : "ipad", 90 | "filename" : "icon_144.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "icon_76.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "icon_152.png", 103 | "scale" : "2x" 104 | } 105 | ], 106 | "info" : { 107 | "version" : 1, 108 | "author" : "xcode" 109 | } 110 | } -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/AppIcon.appiconset/icon_100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/task_lib/4305e770d295ea96b79c9be0d6921cbc089f9630/test/all/ios/Images.xcassets/AppIcon.appiconset/icon_100.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/AppIcon.appiconset/icon_114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/task_lib/4305e770d295ea96b79c9be0d6921cbc089f9630/test/all/ios/Images.xcassets/AppIcon.appiconset/icon_114.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/AppIcon.appiconset/icon_120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/task_lib/4305e770d295ea96b79c9be0d6921cbc089f9630/test/all/ios/Images.xcassets/AppIcon.appiconset/icon_120.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/AppIcon.appiconset/icon_144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/task_lib/4305e770d295ea96b79c9be0d6921cbc089f9630/test/all/ios/Images.xcassets/AppIcon.appiconset/icon_144.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/AppIcon.appiconset/icon_152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/task_lib/4305e770d295ea96b79c9be0d6921cbc089f9630/test/all/ios/Images.xcassets/AppIcon.appiconset/icon_152.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/AppIcon.appiconset/icon_180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/task_lib/4305e770d295ea96b79c9be0d6921cbc089f9630/test/all/ios/Images.xcassets/AppIcon.appiconset/icon_180.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/AppIcon.appiconset/icon_29-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/task_lib/4305e770d295ea96b79c9be0d6921cbc089f9630/test/all/ios/Images.xcassets/AppIcon.appiconset/icon_29-1.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/AppIcon.appiconset/icon_29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/task_lib/4305e770d295ea96b79c9be0d6921cbc089f9630/test/all/ios/Images.xcassets/AppIcon.appiconset/icon_29.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/AppIcon.appiconset/icon_40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/task_lib/4305e770d295ea96b79c9be0d6921cbc089f9630/test/all/ios/Images.xcassets/AppIcon.appiconset/icon_40.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/AppIcon.appiconset/icon_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/task_lib/4305e770d295ea96b79c9be0d6921cbc089f9630/test/all/ios/Images.xcassets/AppIcon.appiconset/icon_50.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/AppIcon.appiconset/icon_57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/task_lib/4305e770d295ea96b79c9be0d6921cbc089f9630/test/all/ios/Images.xcassets/AppIcon.appiconset/icon_57.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/AppIcon.appiconset/icon_58-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/task_lib/4305e770d295ea96b79c9be0d6921cbc089f9630/test/all/ios/Images.xcassets/AppIcon.appiconset/icon_58-1.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/AppIcon.appiconset/icon_58.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/task_lib/4305e770d295ea96b79c9be0d6921cbc089f9630/test/all/ios/Images.xcassets/AppIcon.appiconset/icon_58.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/AppIcon.appiconset/icon_72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/task_lib/4305e770d295ea96b79c9be0d6921cbc089f9630/test/all/ios/Images.xcassets/AppIcon.appiconset/icon_72.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/AppIcon.appiconset/icon_76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/task_lib/4305e770d295ea96b79c9be0d6921cbc089f9630/test/all/ios/Images.xcassets/AppIcon.appiconset/icon_76.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/AppIcon.appiconset/icon_80-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/task_lib/4305e770d295ea96b79c9be0d6921cbc089f9630/test/all/ios/Images.xcassets/AppIcon.appiconset/icon_80-1.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/AppIcon.appiconset/icon_80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/task_lib/4305e770d295ea96b79c9be0d6921cbc089f9630/test/all/ios/Images.xcassets/AppIcon.appiconset/icon_80.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "filename" : "launch_640_960.png", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "extent" : "full-screen", 13 | "idiom" : "iphone", 14 | "subtype" : "retina4", 15 | "filename" : "launch_640_1136.png", 16 | "minimum-system-version" : "7.0", 17 | "orientation" : "portrait", 18 | "scale" : "2x" 19 | }, 20 | { 21 | "orientation" : "portrait", 22 | "idiom" : "ipad", 23 | "extent" : "full-screen", 24 | "minimum-system-version" : "7.0", 25 | "filename" : "launch_768_1024.png", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "orientation" : "landscape", 30 | "idiom" : "ipad", 31 | "extent" : "full-screen", 32 | "minimum-system-version" : "7.0", 33 | "filename" : "launch_1024_768.png", 34 | "scale" : "1x" 35 | }, 36 | { 37 | "orientation" : "portrait", 38 | "idiom" : "ipad", 39 | "extent" : "full-screen", 40 | "minimum-system-version" : "7.0", 41 | "filename" : "launch_1536_2048.png", 42 | "scale" : "2x" 43 | }, 44 | { 45 | "orientation" : "landscape", 46 | "idiom" : "ipad", 47 | "extent" : "full-screen", 48 | "minimum-system-version" : "7.0", 49 | "filename" : "launch_2048_1536.png", 50 | "scale" : "2x" 51 | }, 52 | { 53 | "orientation" : "portrait", 54 | "idiom" : "iphone", 55 | "extent" : "full-screen", 56 | "filename" : "launch_320_480.png", 57 | "scale" : "1x" 58 | }, 59 | { 60 | "orientation" : "portrait", 61 | "idiom" : "iphone", 62 | "extent" : "full-screen", 63 | "filename" : "launch_640_960.png", 64 | "scale" : "2x" 65 | }, 66 | { 67 | "orientation" : "portrait", 68 | "idiom" : "iphone", 69 | "extent" : "full-screen", 70 | "filename" : "launch_640_1136.png", 71 | "subtype" : "retina4", 72 | "scale" : "2x" 73 | }, 74 | { 75 | "orientation" : "portrait", 76 | "idiom" : "ipad", 77 | "extent" : "to-status-bar", 78 | "filename" : "launch_768_1004.png", 79 | "scale" : "1x" 80 | }, 81 | { 82 | "orientation" : "landscape", 83 | "idiom" : "ipad", 84 | "extent" : "to-status-bar", 85 | "filename" : "launch_1024_748.png", 86 | "scale" : "1x" 87 | }, 88 | { 89 | "orientation" : "portrait", 90 | "idiom" : "ipad", 91 | "extent" : "to-status-bar", 92 | "filename" : "launch_1536_2008.png", 93 | "scale" : "2x" 94 | }, 95 | { 96 | "orientation" : "landscape", 97 | "idiom" : "ipad", 98 | "extent" : "to-status-bar", 99 | "filename" : "launch_2048_1496.png", 100 | "scale" : "2x" 101 | } 102 | ], 103 | "info" : { 104 | "version" : 1, 105 | "author" : "xcode" 106 | } 107 | } -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/LaunchImage.launchimage/launch_1024_748.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/task_lib/4305e770d295ea96b79c9be0d6921cbc089f9630/test/all/ios/Images.xcassets/LaunchImage.launchimage/launch_1024_748.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/LaunchImage.launchimage/launch_1024_768.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/task_lib/4305e770d295ea96b79c9be0d6921cbc089f9630/test/all/ios/Images.xcassets/LaunchImage.launchimage/launch_1024_768.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/LaunchImage.launchimage/launch_1536_2008.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/task_lib/4305e770d295ea96b79c9be0d6921cbc089f9630/test/all/ios/Images.xcassets/LaunchImage.launchimage/launch_1536_2008.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/LaunchImage.launchimage/launch_1536_2048.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/task_lib/4305e770d295ea96b79c9be0d6921cbc089f9630/test/all/ios/Images.xcassets/LaunchImage.launchimage/launch_1536_2048.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/LaunchImage.launchimage/launch_2048_1496.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/task_lib/4305e770d295ea96b79c9be0d6921cbc089f9630/test/all/ios/Images.xcassets/LaunchImage.launchimage/launch_2048_1496.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/LaunchImage.launchimage/launch_2048_1536.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/task_lib/4305e770d295ea96b79c9be0d6921cbc089f9630/test/all/ios/Images.xcassets/LaunchImage.launchimage/launch_2048_1536.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/LaunchImage.launchimage/launch_320_480.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/task_lib/4305e770d295ea96b79c9be0d6921cbc089f9630/test/all/ios/Images.xcassets/LaunchImage.launchimage/launch_320_480.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/LaunchImage.launchimage/launch_640_1136.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/task_lib/4305e770d295ea96b79c9be0d6921cbc089f9630/test/all/ios/Images.xcassets/LaunchImage.launchimage/launch_640_1136.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/LaunchImage.launchimage/launch_640_960.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/task_lib/4305e770d295ea96b79c9be0d6921cbc089f9630/test/all/ios/Images.xcassets/LaunchImage.launchimage/launch_640_960.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/LaunchImage.launchimage/launch_768_1004.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/task_lib/4305e770d295ea96b79c9be0d6921cbc089f9630/test/all/ios/Images.xcassets/LaunchImage.launchimage/launch_768_1004.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/LaunchImage.launchimage/launch_768_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/task_lib/4305e770d295ea96b79c9be0d6921cbc089f9630/test/all/ios/Images.xcassets/LaunchImage.launchimage/launch_768_1024.png -------------------------------------------------------------------------------- /test/all/ios/test-all.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.maniccoder.task.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | Foundation 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | TEST 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | NSMainNibFile 26 | test-all 27 | NSMainNibFile~ipad 28 | test-all 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UIStatusBarHidden 34 | 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationLandscapeLeft 39 | UIInterfaceOrientationLandscapeRight 40 | UIInterfaceOrientationPortraitUpsideDown 41 | 42 | UISupportedInterfaceOrientations~ipad 43 | 44 | UIInterfaceOrientationPortrait 45 | UIInterfaceOrientationPortraitUpsideDown 46 | UIInterfaceOrientationLandscapeLeft 47 | UIInterfaceOrientationLandscapeRight 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /test/all/ios/test-all.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /test/all/ios/viewcontroller.h: -------------------------------------------------------------------------------- 1 | /* viewcontroller.h - Foundation test launcher - Public Domain - 2013 Mattias Jansson 2 | * 3 | * This library provides a cross-platform foundation library in C11 providing basic support 4 | * data types and functions to write applications and games in a platform-independent fashion. 5 | * The latest source code is always available at 6 | * 7 | * https://github.com/mjansson/foundation_lib 8 | * 9 | * This library is put in the public domain; you can redistribute it and/or modify it without 10 | * any restrictions. 11 | */ 12 | 13 | #pragma once 14 | 15 | #include 16 | #include 17 | 18 | #ifdef __OBJC__ 19 | 20 | @interface ViewController : UIViewController { 21 | @public 22 | } 23 | @end 24 | 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /test/all/ios/viewcontroller.m: -------------------------------------------------------------------------------- 1 | /* viewcontroller.m - Foundation test launcher - Public Domain - 2013 Mattias Jansson 2 | * 3 | * This library provides a cross-platform foundation library in C11 providing basic support 4 | * data types and functions to write applications and games in a platform-independent fashion. 5 | * The latest source code is always available at 6 | * 7 | * https://github.com/mjansson/foundation_lib 8 | * 9 | * This library is put in the public domain; you can redistribute it and/or modify it without 10 | * any restrictions. 11 | */ 12 | 13 | #include "viewcontroller.h" 14 | 15 | @interface ViewController() 16 | @end 17 | 18 | @implementation ViewController 19 | 20 | - (void)viewDidLoad { 21 | [super viewDidLoad]; 22 | 23 | if ([self respondsToSelector:@selector(setNeedsStatusBarAppearanceUpdate)]) 24 | [self setNeedsStatusBarAppearanceUpdate]; 25 | else 26 | [[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationNone]; 27 | } 28 | 29 | - (void)didReceiveMemoryWarning { 30 | [super didReceiveMemoryWarning]; 31 | } 32 | 33 | - (BOOL)prefersStatusBarHidden { 34 | return TRUE; 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /test/all/main.c: -------------------------------------------------------------------------------- 1 | /* main.c - Foundation test launcher - Public Domain - 2013 Mattias Jansson 2 | * 3 | * This library provides a cross-platform foundation library in C11 providing basic support 4 | * data types and functions to write applications and games in a platform-independent fashion. 5 | * The latest source code is always available at 6 | * 7 | * https://github.com/mjansson/foundation_lib 8 | * 9 | * This library is put in the public domain; you can redistribute it and/or modify it without 10 | * any restrictions. 11 | */ 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | static volatile bool test_should_start_flag; 18 | static volatile bool test_have_focus_flag; 19 | static volatile bool test_should_terminate_flag; 20 | static volatile bool test_memory_tracker; 21 | 22 | static void* 23 | event_loop(void* arg) { 24 | event_block_t* block; 25 | event_t* event = 0; 26 | FOUNDATION_UNUSED(arg); 27 | 28 | event_stream_set_beacon(system_event_stream(), &thread_self()->beacon); 29 | 30 | while (!test_should_terminate_flag) { 31 | block = event_stream_process(system_event_stream()); 32 | event = 0; 33 | while ((event = event_next(block, event))) { 34 | switch (event->id) { 35 | case FOUNDATIONEVENT_START: 36 | #if FOUNDATION_PLATFORM_IOS || FOUNDATION_PLATFORM_ANDROID 37 | log_debug(HASH_TEST, STRING_CONST("Application start event received")); 38 | test_should_start_flag = true; 39 | #endif 40 | break; 41 | 42 | case FOUNDATIONEVENT_TERMINATE: 43 | #if FOUNDATION_PLATFORM_IOS || FOUNDATION_PLATFORM_ANDROID 44 | log_debug(HASH_TEST, STRING_CONST("Application stop/terminate event received")); 45 | test_should_terminate_flag = true; 46 | break; 47 | #else 48 | log_warn(HASH_TEST, WARNING_SUSPICIOUS, STRING_CONST("Terminating tests due to event")); 49 | process_exit(-2); 50 | #endif 51 | 52 | case FOUNDATIONEVENT_FOCUS_GAIN: 53 | test_have_focus_flag = true; 54 | break; 55 | 56 | case FOUNDATIONEVENT_FOCUS_LOST: 57 | test_have_focus_flag = false; 58 | break; 59 | 60 | default: 61 | break; 62 | } 63 | } 64 | thread_wait(); 65 | } 66 | 67 | log_debug(HASH_TEST, STRING_CONST("Application event thread exiting")); 68 | 69 | return 0; 70 | } 71 | 72 | #if (FOUNDATION_PLATFORM_IOS || FOUNDATION_PLATFORM_ANDROID) && BUILD_ENABLE_LOG 73 | 74 | #if FOUNDATION_PLATFORM_ANDROID 75 | #include 76 | #include 77 | #endif 78 | 79 | #include 80 | #include 81 | 82 | static void 83 | test_log_handler(hash_t context, error_level_t severity, const char* msg, size_t length) { 84 | FOUNDATION_UNUSED(context); 85 | FOUNDATION_UNUSED(severity); 86 | 87 | if (test_should_terminate_flag) 88 | return; 89 | 90 | #if FOUNDATION_PLATFORM_IOS 91 | test_text_view_append(delegate_uiwindow(), 1, msg, length); 92 | #elif FOUNDATION_PLATFORM_ANDROID 93 | jclass test_log_class = 0; 94 | jmethodID test_log_append = 0; 95 | const struct JNINativeInterface** jnienv = thread_attach_jvm(); 96 | test_log_class = (*jnienv)->GetObjectClass(jnienv, android_app()->activity->clazz); 97 | if (test_log_class) 98 | test_log_append = (*jnienv)->GetMethodID(jnienv, test_log_class, "appendLog", "(Ljava/lang/String;)V"); 99 | if (test_log_append) { 100 | jstring jstr = (*jnienv)->NewStringUTF(jnienv, msg); 101 | (*jnienv)->CallVoidMethod(jnienv, android_app()->activity->clazz, test_log_append, jstr); 102 | (*jnienv)->DeleteLocalRef(jnienv, jstr); 103 | } 104 | thread_detach_jvm(); 105 | FOUNDATION_UNUSED(length); 106 | #endif 107 | } 108 | 109 | #endif 110 | 111 | #if !BUILD_MONOLITHIC 112 | 113 | void 114 | test_exception_handler(const char* dump_file, size_t length) { 115 | FOUNDATION_UNUSED(dump_file); 116 | FOUNDATION_UNUSED(length); 117 | log_error(HASH_TEST, ERROR_EXCEPTION, STRING_CONST("Test raised exception")); 118 | process_exit(-1); 119 | } 120 | 121 | #endif 122 | 123 | bool 124 | test_should_terminate(void) { 125 | return test_should_terminate_flag; 126 | } 127 | 128 | int 129 | main_initialize(void) { 130 | foundation_config_t config; 131 | application_t application; 132 | int ret; 133 | size_t iarg, asize; 134 | const string_const_t* cmdline = environment_command_line(); 135 | 136 | test_memory_tracker = true; 137 | for (iarg = 0, asize = array_size(cmdline); iarg < asize; ++iarg) { 138 | if (string_equal(STRING_ARGS(cmdline[iarg]), STRING_CONST("--no-memory-tracker"))) 139 | test_memory_tracker = false; 140 | } 141 | 142 | if (test_memory_tracker) 143 | memory_set_tracker(memory_tracker_local()); 144 | 145 | memset(&config, 0, sizeof(config)); 146 | 147 | memset(&application, 0, sizeof(application)); 148 | application.name = string_const(STRING_CONST("Task library test suite")); 149 | application.short_name = string_const(STRING_CONST("test_all")); 150 | application.company = string_const(STRING_CONST("")); 151 | application.version = task_module_version(); 152 | application.flags = APPLICATION_UTILITY; 153 | application.exception_handler = test_exception_handler; 154 | 155 | log_set_suppress(0, ERRORLEVEL_INFO); 156 | 157 | #if (FOUNDATION_PLATFORM_IOS || FOUNDATION_PLATFORM_ANDROID) && BUILD_ENABLE_LOG 158 | log_set_handler(test_log_handler); 159 | #endif 160 | 161 | #if !FOUNDATION_PLATFORM_IOS && !FOUNDATION_PLATFORM_ANDROID 162 | 163 | test_should_start_flag = true; 164 | 165 | #endif 166 | 167 | ret = foundation_initialize(memory_system_malloc(), application, config); 168 | 169 | #if BUILD_MONOLITHIC 170 | if (ret == 0) { 171 | task_config_t task_config; 172 | memset(&task_config, 0, sizeof(task_config)); 173 | ret = task_module_initialize(task_config); 174 | 175 | test_set_suitable_working_directory(); 176 | } 177 | #endif 178 | return ret; 179 | } 180 | 181 | #if FOUNDATION_PLATFORM_ANDROID 182 | #include 183 | #endif 184 | 185 | #if BUILD_MONOLITHIC 186 | extern int 187 | test_task_run(void); 188 | typedef int (*test_run_fn)(void); 189 | 190 | static void* 191 | test_runner(void* arg) { 192 | test_run_fn* tests = (test_run_fn*)arg; 193 | int test_fn = 0; 194 | int process_result = 0; 195 | 196 | while (tests[test_fn] && (process_result >= 0)) { 197 | if ((process_result = tests[test_fn]()) >= 0) 198 | log_infof(HASH_TEST, STRING_CONST("All tests passed (%d)"), process_result); 199 | ++test_fn; 200 | } 201 | 202 | return (void*)(intptr_t)process_result; 203 | } 204 | 205 | #endif 206 | 207 | int 208 | main_run(void* main_arg) { 209 | #if !BUILD_MONOLITHIC 210 | string_const_t pattern; 211 | string_t* exe_paths = 0; 212 | size_t iexe, exesize; 213 | process_t* process = 0; 214 | string_t process_path = {0, 0}; 215 | unsigned int* exe_flags = 0; 216 | #else 217 | void* test_result; 218 | #endif 219 | #if FOUNDATION_PLATFORM_IOS || FOUNDATION_PLATFORM_ANDROID 220 | int remain_counter = 0; 221 | #endif 222 | #if BUILD_DEBUG 223 | const string_const_t build_name = string_const(STRING_CONST("debug")); 224 | #elif BUILD_RELEASE 225 | const string_const_t build_name = string_const(STRING_CONST("release")); 226 | #elif BUILD_PROFILE 227 | const string_const_t build_name = string_const(STRING_CONST("profile")); 228 | #elif BUILD_DEPLOY 229 | const string_const_t build_name = string_const(STRING_CONST("deploy")); 230 | #endif 231 | #if BUILD_MONOLITHIC 232 | const string_const_t build_type = string_const(STRING_CONST(" monolithic")); 233 | #else 234 | const string_const_t build_type = string_empty(); 235 | #endif 236 | char* pathbuf; 237 | int process_result = 0; 238 | thread_t event_thread; 239 | FOUNDATION_UNUSED(main_arg); 240 | FOUNDATION_UNUSED(build_name); 241 | 242 | log_set_suppress(HASH_TEST, ERRORLEVEL_DEBUG); 243 | 244 | log_infof(HASH_TEST, STRING_CONST("Task library v%s built for %s using %s (%.*s%.*s)"), 245 | string_from_version_static(task_module_version()).str, FOUNDATION_PLATFORM_DESCRIPTION, 246 | FOUNDATION_COMPILER_DESCRIPTION, STRING_FORMAT(build_name), STRING_FORMAT(build_type)); 247 | 248 | thread_initialize(&event_thread, event_loop, 0, STRING_CONST("event_thread"), THREAD_PRIORITY_NORMAL, 0); 249 | thread_start(&event_thread); 250 | 251 | pathbuf = memory_allocate(HASH_STRING, BUILD_MAX_PATHLEN, 0, MEMORY_PERSISTENT); 252 | 253 | while (!thread_is_running(&event_thread)) 254 | thread_sleep(10); 255 | 256 | #if FOUNDATION_PLATFORM_IOS || FOUNDATION_PLATFORM_ANDROID 257 | while (!test_should_start_flag) { 258 | #if FOUNDATION_PLATFORM_ANDROID 259 | system_process_events(); 260 | #endif 261 | thread_sleep(100); 262 | } 263 | #endif 264 | 265 | fs_remove_directory(STRING_ARGS(environment_temporary_directory())); 266 | 267 | #if BUILD_MONOLITHIC 268 | 269 | test_run_fn tests[] = {test_task_run, 0}; 270 | 271 | #if FOUNDATION_PLATFORM_ANDROID 272 | 273 | thread_t test_thread; 274 | thread_initialize(&test_thread, test_runner, tests, STRING_CONST("test_runner"), THREAD_PRIORITY_NORMAL, 0); 275 | thread_start(&test_thread); 276 | 277 | log_debug(HASH_TEST, STRING_CONST("Starting test runner thread")); 278 | 279 | while (!thread_is_running(&test_thread)) { 280 | system_process_events(); 281 | thread_sleep(10); 282 | } 283 | 284 | while (thread_is_running(&test_thread)) { 285 | system_process_events(); 286 | thread_sleep(10); 287 | } 288 | 289 | test_result = thread_join(&test_thread); 290 | process_result = (int)(intptr_t)test_result; 291 | 292 | thread_finalize(&test_thread); 293 | 294 | #else 295 | 296 | test_result = test_runner(tests); 297 | process_result = (int)(intptr_t)test_result; 298 | 299 | #endif 300 | 301 | if (process_result != 0) 302 | log_warnf(HASH_TEST, WARNING_SUSPICIOUS, STRING_CONST("Tests failed with exit code %d"), process_result); 303 | 304 | #if FOUNDATION_PLATFORM_IOS || FOUNDATION_PLATFORM_ANDROID 305 | 306 | while (!test_should_terminate() && test_have_focus() && (remain_counter < 50)) { 307 | system_process_events(); 308 | thread_sleep(100); 309 | ++remain_counter; 310 | } 311 | 312 | #endif 313 | 314 | log_debug(HASH_TEST, STRING_CONST("Exiting main loop")); 315 | 316 | #else // !BUILD_MONOLITHIC 317 | 318 | // Find all test executables in the current executable directory 319 | #if FOUNDATION_PLATFORM_WINDOWS 320 | pattern = string_const(STRING_CONST("^test-.*\\.exe$")); 321 | #elif FOUNDATION_PLATFORM_MACOS 322 | pattern = string_const(STRING_CONST("^test-.*$")); 323 | #elif FOUNDATION_PLATFORM_POSIX 324 | pattern = string_const(STRING_CONST("^test-.*$")); 325 | #else 326 | #error Not implemented 327 | #endif 328 | exe_paths = fs_matching_files(STRING_ARGS(environment_executable_directory()), STRING_ARGS(pattern), false); 329 | array_resize(exe_flags, array_size(exe_paths)); 330 | memset(exe_flags, 0, sizeof(unsigned int) * array_size(exe_flags)); 331 | #if FOUNDATION_PLATFORM_MACOS 332 | // Also search for test applications 333 | string_const_t app_pattern = string_const(STRING_CONST("^test-.*\\.app$")); 334 | regex_t* app_regex = regex_compile(app_pattern.str, app_pattern.length); 335 | string_t* subdirs = fs_subdirs(STRING_ARGS(environment_executable_directory())); 336 | for (size_t idir = 0, dirsize = array_size(subdirs); idir < dirsize; ++idir) { 337 | if (regex_match(app_regex, subdirs[idir].str, subdirs[idir].length, 0, 0)) { 338 | string_t exe_path = {subdirs[idir].str, subdirs[idir].length - 4}; 339 | array_push(exe_paths, exe_path); 340 | array_push(exe_flags, PROCESS_MACOS_USE_OPENAPPLICATION); 341 | } 342 | } 343 | string_array_deallocate(subdirs); 344 | regex_deallocate(app_regex); 345 | #endif 346 | for (iexe = 0, exesize = array_size(exe_paths); iexe < exesize; ++iexe) { 347 | string_const_t* process_args = 0; 348 | string_const_t exe_file_name = path_base_file_name(STRING_ARGS(exe_paths[iexe])); 349 | if (string_equal(STRING_ARGS(exe_file_name), STRING_ARGS(environment_executable_name()))) 350 | continue; // Don't run self 351 | 352 | process_path = path_concat(pathbuf, BUILD_MAX_PATHLEN, STRING_ARGS(environment_executable_directory()), 353 | STRING_ARGS(exe_paths[iexe])); 354 | process = process_allocate(); 355 | 356 | process_set_executable_path(process, STRING_ARGS(process_path)); 357 | process_set_working_directory(process, STRING_ARGS(environment_executable_directory())); 358 | process_set_flags(process, PROCESS_ATTACHED | exe_flags[iexe]); 359 | 360 | if (!test_memory_tracker) 361 | array_push(process_args, string_const(STRING_CONST("--no-memory-tracker"))); 362 | process_set_arguments(process, process_args, array_size(process_args)); 363 | 364 | log_infof(HASH_TEST, STRING_CONST("Running test executable: %.*s"), STRING_FORMAT(exe_paths[iexe])); 365 | 366 | process_result = process_spawn(process); 367 | while (process_result == PROCESS_WAIT_INTERRUPTED) { 368 | thread_sleep(10); 369 | process_result = process_wait(process); 370 | } 371 | process_deallocate(process); 372 | array_deallocate(process_args); 373 | 374 | if (process_result != 0) { 375 | if (process_result >= PROCESS_INVALID_ARGS) 376 | log_warnf(HASH_TEST, WARNING_SUSPICIOUS, STRING_CONST("Tests failed, process terminated with error %x"), 377 | process_result); 378 | else 379 | log_warnf(HASH_TEST, WARNING_SUSPICIOUS, STRING_CONST("Tests failed with exit code %d"), 380 | process_result); 381 | process_set_exit_code(-1); 382 | goto exit; 383 | } 384 | 385 | log_infof(HASH_TEST, STRING_CONST("All tests from %.*s passed (%d)"), STRING_FORMAT(exe_paths[iexe]), 386 | process_result); 387 | } 388 | 389 | log_info(HASH_TEST, STRING_CONST("All tests passed")); 390 | 391 | exit: 392 | 393 | if (exe_paths) 394 | string_array_deallocate(exe_paths); 395 | array_deallocate(exe_flags); 396 | 397 | #endif 398 | 399 | test_should_terminate_flag = true; 400 | 401 | thread_signal(&event_thread); 402 | thread_finalize(&event_thread); 403 | 404 | memory_deallocate(pathbuf); 405 | 406 | log_infof(HASH_TEST, STRING_CONST("Tests exiting: %s (%d)"), process_result ? "FAILED" : "PASSED", process_result); 407 | 408 | if (process_result) 409 | memory_set_tracker(memory_tracker_none()); 410 | 411 | return process_result; 412 | } 413 | 414 | void 415 | main_finalize(void) { 416 | #if FOUNDATION_PLATFORM_ANDROID 417 | thread_detach_jvm(); 418 | #endif 419 | 420 | foundation_finalize(); 421 | } 422 | -------------------------------------------------------------------------------- /test/all/tizen/res/tizenapp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/task_lib/4305e770d295ea96b79c9be0d6921cbc089f9630/test/all/tizen/res/tizenapp.png -------------------------------------------------------------------------------- /test/all/tizen/tizen-manifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | tizenapp.png 6 | 7 | 8 | 9 | http://tizen.org/privilege/systemsettings 10 | 11 | 12 | -------------------------------------------------------------------------------- /test/task/main.c: -------------------------------------------------------------------------------- 1 | /* main.c - Task test for task library - MIT License - 2014 Mattias Jansson 2 | * 3 | * This library provides a cross-platform library in C11 providing 4 | * task-based parallellism for projects based on our foundation library. 5 | * 6 | * The latest source code maintained by Mattias Jansson is always available at 7 | * 8 | * https://github.com/mjansson/task_lib 9 | * 10 | * The foundation library source code maintained by Mattias Jansson is always available at 11 | * 12 | * https://github.com/mjansson/foundation_lib 13 | * 14 | * This library is put in the public domain; you can redistribute it and/or modify it without any restrictions. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | static application_t 22 | test_task_application(void) { 23 | application_t app; 24 | memset(&app, 0, sizeof(app)); 25 | app.name = string_const(STRING_CONST("Task tests")); 26 | app.short_name = string_const(STRING_CONST("test_task")); 27 | app.company = string_const(STRING_CONST("")); 28 | app.flags = APPLICATION_UTILITY; 29 | app.exception_handler = test_exception_handler; 30 | return app; 31 | } 32 | 33 | static memory_system_t 34 | test_task_memory_system(void) { 35 | return memory_system_malloc(); 36 | } 37 | 38 | static foundation_config_t 39 | test_task_config(void) { 40 | foundation_config_t config; 41 | memset(&config, 0, sizeof(config)); 42 | return config; 43 | } 44 | 45 | static int 46 | test_task_initialize(void) { 47 | task_config_t config; 48 | log_set_suppress(HASH_TASK, ERRORLEVEL_NONE); 49 | memset(&config, 0, sizeof(config)); 50 | config.fiber_stack_size = 16 * 1024; 51 | return task_module_initialize(config); 52 | } 53 | 54 | static void 55 | test_task_finalize(void) { 56 | task_module_finalize(); 57 | } 58 | 59 | static task_scheduler_t* task_scheduler; 60 | static atomic32_t task_counter; 61 | static atomic32_t remain_counter; 62 | 63 | static struct multi_task_setup_t { 64 | size_t sub_task_count; 65 | size_t final_task_count; 66 | } multi_task_setup; 67 | 68 | static FOUNDATION_NOINLINE void 69 | task_single_test(task_context_t context) { 70 | FOUNDATION_UNUSED(context); 71 | // log_infof(HASH_TASK, STRING_CONST("Task executing %d"), atomic_load32(&task_counter, memory_order_relaxed)); 72 | atomic_incr32(&task_counter, memory_order_relaxed); 73 | } 74 | 75 | static FOUNDATION_NOINLINE void 76 | task_multi_sub_test(task_context_t context) { 77 | struct multi_task_setup_t* setup = (struct multi_task_setup_t*)context; 78 | size_t sub_task_count = setup->final_task_count; 79 | atomic32_t sub_counter; 80 | atomic_store32(&sub_counter, (int32_t)sub_task_count, memory_order_relaxed); 81 | 82 | task_t* sub_task = memory_allocate(HASH_TEST, sizeof(task_t) * sub_task_count, 0, MEMORY_PERSISTENT); 83 | for (size_t itask = 0; itask < sub_task_count; ++itask) { 84 | sub_task[itask].function = task_single_test; 85 | sub_task[itask].context = 0; 86 | sub_task[itask].counter = &sub_counter; 87 | } 88 | 89 | task_scheduler_multiqueue(task_scheduler, sub_task, sub_task_count); 90 | 91 | memory_deallocate(sub_task); 92 | 93 | task_yield_and_wait(&sub_counter); 94 | 95 | FOUNDATION_ASSERT(atomic_load32(&sub_counter, memory_order_relaxed) == 0); 96 | } 97 | 98 | static FOUNDATION_NOINLINE void 99 | task_multi_test(task_context_t context) { 100 | struct multi_task_setup_t* setup = (struct multi_task_setup_t*)context; 101 | size_t sub_task_count = setup->sub_task_count; 102 | task_t* sub_task = memory_allocate(HASH_TEST, sizeof(task_t) * sub_task_count, 0, MEMORY_PERSISTENT); 103 | 104 | atomic32_t sub_counter; 105 | atomic_store32(&sub_counter, (int32_t)sub_task_count, memory_order_relaxed); 106 | for (size_t itask = 0; itask < sub_task_count; ++itask) { 107 | sub_task[itask].function = task_multi_sub_test; 108 | sub_task[itask].context = (task_context_t)setup; 109 | sub_task[itask].counter = &sub_counter; 110 | } 111 | 112 | task_scheduler_multiqueue(task_scheduler, sub_task, sub_task_count); 113 | task_yield_and_wait(&sub_counter); 114 | 115 | FOUNDATION_ASSERT(atomic_load32(&sub_counter, memory_order_relaxed) == 0); 116 | 117 | atomic_store32(&sub_counter, (int32_t)sub_task_count, memory_order_relaxed); 118 | memset(sub_task, 0xFF, sizeof(task_t) * sub_task_count); 119 | for (size_t itask = 0; itask < sub_task_count; ++itask) { 120 | sub_task[itask].function = task_multi_sub_test; 121 | sub_task[itask].context = (task_context_t)setup; 122 | sub_task[itask].counter = &sub_counter; 123 | } 124 | 125 | task_scheduler_multiqueue(task_scheduler, sub_task, sub_task_count); 126 | task_yield_and_wait(&sub_counter); 127 | 128 | FOUNDATION_ASSERT(atomic_load32(&sub_counter, memory_order_relaxed) == 0); 129 | 130 | memory_deallocate(sub_task); 131 | } 132 | 133 | DECLARE_TEST(task, single) { 134 | task_scheduler = task_scheduler_allocate(1 /*system_hardware_threads()*/, 128); 135 | 136 | thread_sleep(100); 137 | 138 | task_t task = {0}; 139 | task.function = task_single_test; 140 | task.counter = &remain_counter; 141 | 142 | atomic_store32(&task_counter, 0, memory_order_relaxed); 143 | atomic_store32(&remain_counter, 1, memory_order_relaxed); 144 | 145 | task_scheduler_queue(task_scheduler, task); 146 | 147 | task_yield_and_wait(&remain_counter); 148 | 149 | task_scheduler_deallocate(task_scheduler); 150 | 151 | EXPECT_EQ(atomic_load32(&task_counter, memory_order_relaxed), 1); 152 | EXPECT_EQ(atomic_load32(&remain_counter, memory_order_relaxed), 0); 153 | 154 | return 0; 155 | } 156 | 157 | DECLARE_TEST(task, multi) { 158 | assert_force_continue(false); 159 | 160 | task_scheduler = task_scheduler_allocate(system_hardware_threads(), 1024); 161 | 162 | thread_sleep(100); 163 | 164 | // Six million tasks in total (20*30*5000*2) 165 | size_t task_count = 20; 166 | multi_task_setup.sub_task_count = 30; 167 | multi_task_setup.final_task_count = 5000; 168 | task_t* task = memory_allocate(HASH_TEST, sizeof(task_t) * task_count, 0, MEMORY_PERSISTENT); 169 | for (size_t itask = 0; itask < task_count; ++itask) { 170 | task[itask].function = task_multi_test; 171 | task[itask].context = (task_context_t)&multi_task_setup; 172 | task[itask].counter = &remain_counter; 173 | } 174 | 175 | atomic_store32(&task_counter, 0, memory_order_relaxed); 176 | atomic_store32(&remain_counter, (int32_t)task_count, memory_order_relaxed); 177 | 178 | task_scheduler_multiqueue(task_scheduler, task, task_count); 179 | 180 | memory_deallocate(task); 181 | 182 | task_yield_and_wait(&remain_counter); 183 | 184 | task_scheduler_deallocate(task_scheduler); 185 | 186 | size_t total_count = task_count * multi_task_setup.sub_task_count * multi_task_setup.final_task_count * 2; 187 | EXPECT_EQ(atomic_load32(&task_counter, memory_order_relaxed), (int32_t)total_count); 188 | EXPECT_EQ(atomic_load32(&remain_counter, memory_order_relaxed), 0); 189 | 190 | return 0; 191 | } 192 | 193 | static atomic32_t directories_searched; 194 | static atomic32_t files_searched; 195 | static atomic32_t files_found; 196 | static atomic32_t file_remain_counter; 197 | 198 | static const char keyword[] = "main"; 199 | 200 | static void 201 | task_find_in_file(task_context_t context) { 202 | char* file_to_search = (char*)context; 203 | 204 | stream_t* stream = stream_open(file_to_search, string_length(file_to_search), STREAM_IN | STREAM_BINARY); 205 | string_deallocate(file_to_search); 206 | 207 | if (!stream) 208 | return; 209 | 210 | error_context_push(STRING_CONST("Find in file"), STRING_ARGS(stream->path)); 211 | 212 | bool keyword_found = false; 213 | size_t buffer_size = 60 * 1024; 214 | char* buffer = memory_allocate(0, buffer_size, 0, MEMORY_PERSISTENT); 215 | while (!keyword_found && !stream_eos(stream)) { 216 | size_t read = stream_read(stream, buffer, buffer_size); 217 | 218 | void* current = buffer; 219 | size_t remain = read; 220 | while (remain) { 221 | void* found = memchr(current, keyword[0], remain); 222 | if (!found) 223 | break; 224 | 225 | size_t offset = (size_t)pointer_diff(found, current); 226 | remain -= offset; 227 | if (remain < sizeof(keyword)) { 228 | if (!stream_eos(stream)) 229 | stream_seek(stream, (ssize_t)sizeof(keyword) - (ssize_t)remain, STREAM_SEEK_CURRENT); 230 | break; 231 | } 232 | 233 | current = found; 234 | if (string_equal(current, sizeof(keyword), keyword, sizeof(keyword))) { 235 | atomic_incr32(&files_found, memory_order_relaxed); 236 | keyword_found = true; 237 | break; 238 | } 239 | current = pointer_offset(current, 1); 240 | --remain; 241 | } 242 | } 243 | 244 | memory_deallocate(buffer); 245 | 246 | error_context_pop(); 247 | 248 | stream_deallocate(stream); 249 | 250 | atomic_incr32(&files_searched, memory_order_relaxed); 251 | } 252 | 253 | static void 254 | task_find_in_files(string_const_t path) { 255 | error_context_push(STRING_CONST("Find in files"), STRING_ARGS(path)); 256 | 257 | /* Files */ 258 | string_t* files = fs_files(STRING_ARGS(path)); 259 | uint filecount = array_count(files); 260 | atomic_add32(&file_remain_counter, (int32_t)filecount, memory_order_relaxed); 261 | for (uint ifile = 0; ifile < filecount; ++ifile) { 262 | task_t subtask = {0}; 263 | subtask.function = task_find_in_file; 264 | subtask.context = (task_context_t)path_allocate_concat(STRING_ARGS(path), STRING_ARGS(files[ifile])).str; 265 | subtask.counter = &file_remain_counter; 266 | task_scheduler_queue(task_scheduler, subtask); 267 | } 268 | 269 | string_array_deallocate(files); 270 | 271 | /* Subdirectories */ 272 | string_t* subdirectories = fs_subdirs(STRING_ARGS(path)); 273 | uint dircount = array_count(subdirectories); 274 | for (uint idir = 0; idir < dircount; ++idir) { 275 | string_t subpath = path_allocate_concat(STRING_ARGS(path), STRING_ARGS(subdirectories[idir])); 276 | task_find_in_files(string_const(STRING_ARGS(subpath))); 277 | string_deallocate(subpath.str); 278 | atomic_incr32(&directories_searched, memory_order_relaxed); 279 | } 280 | 281 | string_array_deallocate(subdirectories); 282 | 283 | error_context_pop(); 284 | } 285 | 286 | DECLARE_TEST(task, find_in_file) { 287 | task_scheduler = task_scheduler_allocate(system_hardware_threads(), 4096); 288 | 289 | atomic_store32(&file_remain_counter, 0, memory_order_relaxed); 290 | 291 | task_find_in_files(environment_current_working_directory()); 292 | 293 | task_yield_and_wait(&file_remain_counter); 294 | 295 | task_scheduler_deallocate(task_scheduler); 296 | 297 | error_level_t suppress = log_suppress(HASH_TEST); 298 | log_set_suppress(HASH_TEST, ERRORLEVEL_DEBUG); 299 | log_infof(HASH_TEST, STRING_CONST("Searched %d files and %d directories, found %d matching files"), 300 | atomic_load32(&files_searched, memory_order_relaxed), 301 | atomic_load32(&directories_searched, memory_order_relaxed), 302 | atomic_load32(&files_found, memory_order_relaxed)); 303 | log_set_suppress(HASH_TEST, suppress); 304 | 305 | return 0; 306 | } 307 | 308 | static void 309 | test_task_declare(void) { 310 | ADD_TEST(task, single); 311 | ADD_TEST(task, multi); 312 | ADD_TEST(task, find_in_file); 313 | } 314 | 315 | static test_suite_t test_task_suite = {test_task_application, test_task_memory_system, test_task_config, 316 | test_task_declare, test_task_initialize, test_task_finalize}; 317 | 318 | #if BUILD_MONOLITHIC 319 | 320 | int 321 | test_task_run(void); 322 | 323 | int 324 | test_task_run(void) { 325 | test_suite = test_task_suite; 326 | return test_run_all(); 327 | } 328 | 329 | #else 330 | 331 | test_suite_t 332 | test_suite_define(void); 333 | 334 | test_suite_t 335 | test_suite_define(void) { 336 | return test_task_suite; 337 | } 338 | 339 | #endif 340 | --------------------------------------------------------------------------------