├── .gitignore ├── CMakeLists.txt ├── LICENSE.GPL ├── README ├── bin ├── ibjom.bat ├── xgejom.bat └── xgejom.xml ├── changelog.txt ├── dist └── createDistZip.pl ├── jom.pro ├── src ├── app │ ├── CMakeLists.txt │ ├── app.pro │ ├── app.qrc │ ├── app.rc.in │ ├── application.cpp │ ├── application.h │ ├── main.cpp │ ├── qt.conf │ └── version.txt └── jomlib │ ├── CMakeLists.txt │ ├── commandexecutor.cpp │ ├── commandexecutor.h │ ├── dependencygraph.cpp │ ├── dependencygraph.h │ ├── exception.cpp │ ├── exception.h │ ├── fastfileinfo.cpp │ ├── fastfileinfo.h │ ├── filetime.cpp │ ├── filetime.h │ ├── helperfunctions.cpp │ ├── helperfunctions.h │ ├── iocompletionport.cpp │ ├── iocompletionport.h │ ├── jobclient.cpp │ ├── jobclient.h │ ├── jobclientacquirehelper.cpp │ ├── jobclientacquirehelper.h │ ├── jobserver.cpp │ ├── jobserver.h │ ├── jomlib.pro │ ├── jomprocess.cpp │ ├── jomprocess.h │ ├── jomprocess_qt.cpp │ ├── macrotable.cpp │ ├── macrotable.h │ ├── makefile.cpp │ ├── makefile.h │ ├── makefilefactory.cpp │ ├── makefilefactory.h │ ├── makefilelinereader.cpp │ ├── makefilelinereader.h │ ├── options.cpp │ ├── options.h │ ├── parser.cpp │ ├── parser.h │ ├── ppexpr-lex.inc │ ├── ppexpr.g │ ├── ppexpr.l │ ├── ppexpr_grammar.cpp │ ├── ppexpr_grammar_p.h │ ├── ppexprparser.cpp │ ├── ppexprparser.h │ ├── preprocessor.cpp │ ├── preprocessor.h │ ├── processenvironment.h │ ├── stable.h │ ├── targetexecutor.cpp │ ├── targetexecutor.h │ └── use_jomlib.pri └── tests ├── CMakeLists.txt ├── makefiles ├── blackbox │ ├── buildUnrelatedTargetsOnError │ │ └── test.mk │ ├── builtins │ │ └── cd.mk │ ├── caseInsensitiveDependents │ │ └── test.mk │ ├── environmentVariables │ │ └── test.mk │ ├── environmentVariablesCaseInsensitivity │ │ └── test.mk │ ├── environmentVariablesInCommands │ │ └── test.mk │ ├── ignoreExitCodes │ │ └── test.mk │ ├── inlineFiles │ │ ├── test.mk │ │ ├── test_basic_expected.txt │ │ ├── test_escaping_expected.txt │ │ └── test_multipleFiles_expected.txt │ ├── macrosOnCommandLine │ │ └── test.mk │ ├── noTargets │ │ └── test.mk │ ├── nonexistentdependent │ │ └── test.mk │ ├── outofdatecheck │ │ └── test.mk │ ├── suffixes │ │ ├── one.a │ │ ├── one.b │ │ ├── one.c │ │ ├── test.mk │ │ ├── three.c │ │ ├── three.d │ │ ├── two.b │ │ └── two.c │ └── unicodeFiles │ │ ├── test_utf16.mk │ │ └── test_utf8.mk ├── circular_include.mk ├── circular_include2.mk ├── commandmodifiers.mk ├── comments.mk ├── conditionals.mk ├── cycle_in_targets.mk ├── depswithspace.mk ├── descriptionblocks.mk ├── dotdirectives.mk ├── file#99.txt ├── fileNameMacrosInDependents.mk ├── filenamemacros.mk ├── foo1.cpp ├── foo3.cpp ├── foo4.cpp ├── include1.mk ├── include2.mk ├── include5.mk ├── include_test.mk ├── infrules.mk ├── macrotest.mk ├── subdir │ ├── foo1.cpp │ ├── foo2.cpp │ ├── foo4.cpp │ ├── include3.mk │ ├── include4.mk │ ├── include7.mk │ ├── include8.mk │ ├── include9.mk │ └── subsub │ │ └── include6.mk ├── targetmultidef.mk ├── wildcardsInDependencies.mk └── windowspaths.mk ├── tests.cpp ├── tests.h └── tests.pro /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | tmp 3 | debug 4 | release 5 | Makefile 6 | Makefile.Debug 7 | Makefile.Release 8 | *.pro.user* 9 | *.lib 10 | *.obj 11 | *.ncb 12 | *.sln 13 | *.suo 14 | *.vcproj* 15 | *.pdb 16 | *.idb 17 | *.ib_pdb_index 18 | *.bak 19 | *.swp 20 | *~ 21 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.6) 2 | project(jom LANGUAGES CXX) 3 | 4 | set(CMAKE_AUTOMOC ON) 5 | enable_testing() 6 | 7 | # where to look first for cmake modules 8 | set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules;${CMAKE_MODULE_PATH}") 9 | 10 | find_package(Qt5 5.2.0 REQUIRED COMPONENTS Core) 11 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) 12 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) 13 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin) 14 | add_subdirectory(src/jomlib) 15 | add_subdirectory(src/app) 16 | 17 | if(BUILD_TESTING) 18 | add_subdirectory(tests) 19 | endif() 20 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | jom - the parallel make tool for Windows 2 | 3 | It's aimed to be an nmake clone with support for parallel builds. 4 | The project's wiki is here: http://wiki.qt.io/Jom 5 | 6 | == How to compile jom == 7 | Jom is a Qt program. The steps to compile jom on command line are: 8 | qmake 9 | nmake 10 | 11 | == How to compile jom with cmake == 12 | We assume that everything we do happens in the folder X:\build-jom 13 | 1. unpack these sources of jom into X:\build-jom\jom 14 | 2. add a build directory somewhere - this normally either is a 15 | directory on the same level as the build directory 16 | or a subdirectory of the source root directory. 17 | !!! Do not try to build from within the source directory !!! 18 | 3. switch into the build directory and run the following commands: 19 | 20 | X:\build-jom\build> cmake X:\build-jom\jom -G "NMake Makefiles" -DCMAKE_PREFIX_PATH=X:\build-jom -DCMAKE_INSTALL_PREFIX=X:\build-jom\install 21 | ... 22 | 23 | To take a special version of Qt, append -DQT_QMAKE_EXECUTABLE:PATH=X:\build-jom\qt\bin\qmake.exe . 24 | 25 | Now just run: 26 | 27 | X:\build-jom\build> nmake && nmake install 28 | ... 29 | 30 | If you want to begin new, just throw away the build dir. 31 | 32 | == Running the tests == 33 | To build the unit tests in jom, add the option -DJOM_ENABLE_TESTS=ON 34 | to the cmake line. 35 | To run the tests, you can simply type 36 | 37 | X:\build-jom\> nmake test 38 | ... 39 | 40 | == Environment variables == 41 | 42 | Like nmake, jom reads default command line arguments from an environment variable: JOMFLAGS. 43 | If JOMFLAGS is not set, MAKEFLAGS is read. 44 | This is useful to set up separate flags for nmake and jom, e.g. 45 | set MAKEFLAGS=L 46 | set JOMFLAGS=Lj8 47 | 48 | == .SYNC dependents == 49 | 50 | You can use the .SYNC directive on the right side of a description 51 | block definition T to prevent jom from running all of T's dependents 52 | in parallel. 53 | For example the following description block adds further dependencies 54 | between its dependents. 55 | 56 | all: Init Prebuild .SYNC Build .SYNC Postbuild 57 | 58 | This adds these additional dependencies: 59 | Build -> Init Prebuild 60 | Postbuild -> Build 61 | 62 | Now the 'Init' and 'Prebuild' targets are built before 'Build'. 63 | 64 | -------------------------------------------------------------------------------- /bin/ibjom.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal enabledelayedexpansion 3 | if "%IBJOM_NUMBEROFJOBS%" == "" set IBJOM_NUMBEROFJOBS=20 4 | BuildConsole /command="jom -j%IBJOM_NUMBEROFJOBS% %*" || exit /b !ERRORLEVEL! 5 | endlocal 6 | -------------------------------------------------------------------------------- /bin/xgejom.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal enabledelayedexpansion 3 | if "%IBJOM_NUMBEROFJOBS%" == "" set IBJOM_NUMBEROFJOBS=60 4 | set NINJAFLAGS=-j60 5 | xgConsole /profile="%~dp0\xgejom.xml" /command="jom -j%IBJOM_NUMBEROFJOBS% %*" || exit /b !ERRORLEVEL! 6 | endlocal 7 | -------------------------------------------------------------------------------- /bin/xgejom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /changelog.txt: -------------------------------------------------------------------------------- 1 | This is the changelog for jom 1.1.4, the parallel make tool. 2 | 3 | Changes since jom 1.1.3 4 | - Fixed handling of the command line option /j. 5 | - Makefiles that do not contain targets are allowed. 6 | - Fixed handling of macro definitions on the command line (QTCREATORBUG-22176). 7 | - Enabled usage of paths longer than MAX_PATH (QTCREATORBUG-22848). 8 | - Added version information to the executable (QTCREATORBUG-23290). 9 | - Fixed directory separators in the MAKEDIR variable (QTCREATORBUG-23559). 10 | - Fixed expansion of absolute path wildcard dependencies (QTCREATORBUG-26745). 11 | - Fixed performance penalty when checking for dependency cycles in complex 12 | Makefiles (QTCREATORBUG-29412). 13 | 14 | Changes since jom 1.1.2 15 | - Removed the /KEEPTEMPFILES option. This option only worked for top-level make 16 | files anyway and was less useful than intended. Use the /U option to display 17 | the content of inline files instead. 18 | - Fixed an issue where jom.exe would try to load qt.conf from drive E. 19 | - Fixed handling of double backslash at the end of line (QTCREATORBUG-20550). 20 | - Fixed handling of line continuations in preprocessor directives 21 | (QTCREATORBUG-8621, QTCREATORBUG-18001). 22 | - Fixed the CMake project file. 23 | 24 | Changes since jom 1.1.1 25 | - Fixed exit code propagation in xgejom.bat and ibjom.bat (QTCREATORBUG-16619). 26 | - Updated the CMake build system to use Qt5. 27 | - Fixed bogus path in error message for failed targets. 28 | - Fixed error "JobClient destroyed while still acquiring." that could be yielded 29 | for failed builds. 30 | - Fixed /k option (QTCREATORBUG-17131). 31 | - Fixed sub-jom detection for chocolatey shims (QTCREATORBUG-15494). 32 | 33 | Changes since jom 1.1.0 34 | - Added the special .NOTPARALLEL target that disables parallel build for the 35 | containing makefile. 36 | - Fixed glitches in !include directives. Includes like <"foo.mk"> were not 37 | possible. Includes like "foo.mk" did not recursively search through parent 38 | makefiles directories. 39 | - Introduced the JOMFLAGS environment variable. This is a MAKEFLAGS pendant. 40 | JOMFLAGS is useful to set up separate flags for nmake and jom. 41 | - Renamed ibjom.cmd to ibjom.bat for consistency. 42 | - Added xgejom.bat, a wrapper for the IncrediBuild XGE interface. 43 | - Fixed wildcard handling for subdirectories (QTCREATORBUG-16499). 44 | - Implemented the "build all targets" option (QTCREATORBUG-16500). 45 | 46 | Changes since jom 1.0.16 47 | - Restrict the number of parallel jobs in recursively called instances 48 | similar to GNU make's jobserver (QTCREATORBUG-10846). 49 | 50 | Changes since jom 1.0.15 51 | - Fix occasional hang on exit (QTCREATORBUG-14600). 52 | 53 | Changes since jom 1.0.14 54 | - Fix potential hang on error in a sub-jom. 55 | - Yield error on missing !endif directives (QTCREATORBUG-14211). 56 | - Fix ignoring exit codes greater than 255. 57 | - Fix /DUMPGRAPHDOT option (QTCREATORBUG-14004). 58 | - Fix $? filename macro for nonexistent targets (QTCREATORBUG-14125). 59 | - Fix quoting of dependencies with spaces (QTCREATORBUG-12296). 60 | 61 | Changes since jom 1.0.13 62 | - Fix exit code forwarding. (QTCREATORBUG-11556) 63 | - Fix file name macro modifers for $** and $?. (QTCREATORBUG-6932) 64 | - Fix equal signs in semicolon command syntax. (QTCREATORBUG-7942) 65 | - Fix error line numbers for inline files. (QTCREATORBUG-8451) 66 | - Ignore macro assignments in inline files. (QTCREATORBUG-8452) 67 | - Yield error on unexpected !ENDIF directive. 68 | - Support wildcards in dependency lines. (QTCREATORBUG-10439) 69 | - Use IcrediBuild's BuildConsole instead of the XGE interface in ibjom. 70 | 71 | Changes since jom 1.0.12 72 | - Fix spurious "Can't start command" error. 73 | - Fix passing of -j argument to subjoms. 74 | - Sanity check the -j argument. 75 | - Fix regression of non-functional set command in command blocks. 76 | - Fix handling of environment variables that would cause a syntax errors in 77 | makefiles. 78 | - Fix handling of environment variables with lowercase names. 79 | 80 | Changes since jom 1.0.11 81 | - Performance improvements. 82 | - Fixed whitespace in command line arguments. (QTCREATORBUG-7428) 83 | - Enhanced responsiveness of subjom output. 84 | - Separate output of stdout and stderr. 85 | - Fix the output of silent commands in /n mode. (QTCREATORBUG-7170) 86 | - Implemented the /U switch for dumping inline files. 87 | - Fix expansion of environment variables. 88 | 89 | Changes since jom 1.0.10 90 | - Fix exit code for /k command line switch. (QTCREATORBUG-6987) 91 | - Fix spurious 0x0d characters in process output. (QTCREATORBUG-6986) 92 | - Fix process output ordering and printing of large amounts of output 93 | in recursive jom scenarios. 94 | - Improved performance by caching file time stamps. 95 | 96 | Changes since jom 1.0.9 97 | - Fix regression that caused jom to fail on Windows XP. 98 | 99 | Changes since jom 1.0.8 100 | - The GNU Make option -w is now supported to print the current working 101 | directory before and after other processing. 102 | - Fix crash when specifying a target for an empty makefile. 103 | (QTCREATORBUG-6552) 104 | - Fix lookup for targets in the Makefile's directory. (QTCREATORBUG-6546) 105 | - Fix handling of environment variables. (QTCREATORBUG-6578) 106 | - Specifying an exit code with the '-' command modifier was fixed. 107 | - Fix command modifiers in inference rules. (QTCREATORBUG-6742) 108 | - Fix parsing of the "ignore exit code" command modifier. 109 | - Fix (very rare) deadlock and zombie processes. 110 | - The preprocessor directive !include now looks up files in the 111 | include directories correctly. (QTCREATORBUG-6869) 112 | 113 | Changes since jom 1.0.7 114 | - Fix spawning of child processes that expect a valid stdin handle. 115 | E.g. xcopy couldn't be called anymore. 116 | - CMakeList.txt was fixed (thanks to Ryan Pavlik). 117 | 118 | Changes since jom 1.0.6 119 | - Fix blank lines in output. (QTCREATORBUG-3974) 120 | - Small performance improvement due to compilation with 121 | QT_USE_FAST_CONCATENATION and QT_USE_FAST_OPERATOR_PLUS. 122 | - Ctrl-C handling has been revisited. Much simpler and more correct. 123 | Before the compiler jobs didn't get a Ctrl-C but were terminated. 124 | - Special .SYNC dependents implemented. See README for a small description. 125 | (QTCREATORBUG-3257) 126 | - Handle quoted dollar signs in inline files correctly. (QTCREATORBUG-2875) 127 | - Respect the MAKEFLAGS environment variable. (QTCREATORBUG-5028) 128 | - Complete detection of cmd builtins. This fixes annoying warnings when building 129 | with IncrediBuild. 130 | 131 | Changes since jom 1.0.5 132 | - Fix mean monster spawn issue. 133 | 134 | Changes since jom 1.0.4 135 | - Fix out of date check for pseudotargets. (QTCREATORBUG-3909) 136 | 137 | Changes since jom 1.0.3 138 | - Fix that -j1 wasn't passed to sub jom calls. (QTCREATORBUG-3729) 139 | - /KEEPTEMPFILES was added for debugging purposes. (QTCREATORBUG-3852) 140 | - Performance boost for incremental builds. 141 | - Improved Ctrl-C handling for recursive make. 142 | 143 | Changes since jom 1.0.2 144 | - Support variable names with underscores. (QTCREATORBUG-3235) 145 | - Keep order of dependencies on build. This is important for makefiles 146 | lacking complete dependencies. These might fail in parallel builds 147 | otherwise. 148 | - Fix issues wrt environment variables. (QTCREATORBUG-3108) 149 | 150 | Changes since jom 1.0.1 151 | - Make target names case insensitive. (QTCREATORBUG-3037) 152 | - Fix reading of include files without newline at the end. 153 | (QTBUG-15021) 154 | - Repair the cmake project file for jom. 155 | 156 | Changes since jom 1.0.0 157 | - Cleaner and faster determination of targets that must be built. 158 | - Fix incomplete up-to-date check for inference rule targets. 159 | (QTCREATORBUG-2713) 160 | - Don't filter comments in inline files. (QTCREATORBUG-2874) 161 | 162 | Changes since jom 0.9.4 163 | - Inference rule batch mode implemented. 164 | This improves performance for makefiles that make heavy use of the batch 165 | mode, e.g. qmake generated makefiles. 166 | - Improved command execution. No more batch file writing. 167 | - Macro substitution implemented. Macro expansions like 168 | $(TARGET:suffix=d) are now possible. 169 | - .SUFFIXES content is handled properly. 170 | - Fixed several bugs in file name macro expansion. 171 | - Output of concurrent processes is explicitely separated. 172 | 173 | Changes since jom 0.9.3 174 | - Opening files from volume mounted to NTFS directory fixed. 175 | (QTCREATORBUG-1475) 176 | - Output buffering issue fixed. (QTBUG-10798) 177 | 178 | Changes since jom 0.9.2 179 | - Support for multiple inline files per command line added. 180 | - Its now possible to use "." as target name. (QTCREATORBUG-1131) 181 | - Support file name macros in the file name part of inline file defitions. 182 | (QTCREATORBUG-1136) 183 | - Multiple file name macros per command line are possible now. 184 | - Single name macro invocation was added. 185 | They can be invoked like this: $A instead of $(A). 186 | - Several bugs related to file name macros have been fixed. 187 | - Handle quoted arguments from command files correctly. 188 | 189 | Changes since jom 0.9.1 190 | - Fixed conditionals in preprocessor expressions. (QTCREATORBUG-1091) 191 | - Improved parser error messages. Now we see the file, in which the 192 | error occurred. (QTCREATORBUG-1114) 193 | 194 | Changes since jom 0.9.0 195 | - Having too many rules for a target is now a warning and not an error. 196 | - Fixed several bugs in the preprocessor expression parser, e.g. 197 | QTCREATORBUG-1056. 198 | 199 | Changes since jom 0.8.9 200 | - Reset the ERRORLEVEL to zero, if command's exit code should be ignored. 201 | (QTCREATORBUG-837) 202 | - Command files can be used to pass command line options to jom. 203 | Usage: jom @commands.txt 204 | - ANTLR isn't used anymore. We're using QLALR to generate the parser for 205 | preprocessor expressions now. This makes building jom much easier. 206 | 207 | -------------------------------------------------------------------------------- /dist/createDistZip.pl: -------------------------------------------------------------------------------- 1 | #!/bin/perl 2 | $jomVersion = `../bin/jom.exe /VERSION`; 3 | $jomVersion = substr($jomVersion, 12); 4 | $jomVersion = substr($jomVersion, 0, -1); 5 | $jomVersion =~ /(\d+)\.(\d+)\.(\d+)/; 6 | $jomVersion = ${1} . "_" . ${2} . "_" . ${3}; 7 | system("copy /Y jom.zip jom_" . $jomVersion . ".zip"); 8 | -------------------------------------------------------------------------------- /jom.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = subdirs 2 | sub_jomlib.subdir = src/jomlib 3 | sub_app.subdir = src/app 4 | sub_app.depends = sub_jomlib 5 | sub_tests.subdir = tests 6 | sub_tests.depends = sub_jomlib sub_app 7 | SUBDIRS = sub_app sub_jomlib sub_tests 8 | 9 | OTHER_FILES = \ 10 | changelog.txt \ 11 | LICENSE.GPL \ 12 | README 13 | 14 | defineTest(minQtVersion) { 15 | maj = $$1 16 | min = $$2 17 | patch = $$3 18 | isEqual(QT_MAJOR_VERSION, $$maj) { 19 | isEqual(QT_MINOR_VERSION, $$min) { 20 | isEqual(QT_PATCH_VERSION, $$patch) { 21 | return(true) 22 | } 23 | greaterThan(QT_PATCH_VERSION, $$patch) { 24 | return(true) 25 | } 26 | } 27 | greaterThan(QT_MINOR_VERSION, $$min) { 28 | return(true) 29 | } 30 | } 31 | greaterThan(QT_MAJOR_VERSION, $$maj) { 32 | return(true) 33 | } 34 | return(false) 35 | } 36 | 37 | !minQtVersion(5, 2, 0) { 38 | message("Cannot build jom with Qt version $${QT_VERSION}.") 39 | error("Use at least Qt 5.2.0.") 40 | } 41 | -------------------------------------------------------------------------------- /src/app/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | file(READ version.txt version) 2 | string(REGEX REPLACE "[\r\n]" "" version ${version}) 3 | string(REPLACE "." ";" version_list ${version}) 4 | list(GET version_list 0 JOM_VERSION_MAJOR) 5 | list(GET version_list 1 JOM_VERSION_MINOR) 6 | list(GET version_list 2 JOM_VERSION_PATCH) 7 | 8 | configure_file( 9 | app.rc.in 10 | app.rc) 11 | 12 | add_executable(jom 13 | application.cpp 14 | application.h 15 | main.cpp 16 | ${CMAKE_CURRENT_BINARY_DIR}/app.rc 17 | ) 18 | 19 | target_compile_definitions(jom 20 | PRIVATE JOM_VERSION_MAJOR=${JOM_VERSION_MAJOR} 21 | PRIVATE JOM_VERSION_MINOR=${JOM_VERSION_MINOR} 22 | PRIVATE JOM_VERSION_PATCH=${JOM_VERSION_PATCH} 23 | ) 24 | 25 | set_target_properties(jom PROPERTIES DEBUG_POSTFIX d) 26 | target_link_libraries(jom PRIVATE jomlib) 27 | 28 | install(TARGETS jom RUNTIME DESTINATION bin 29 | LIBRARY DESTINATION lib 30 | ARCHIVE DESTINATION lib) 31 | -------------------------------------------------------------------------------- /src/app/app.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | DESTDIR = ../../bin 3 | QT = core 4 | CONFIG += console 5 | DEFINES += QT_NO_CAST_FROM_ASCII QT_NO_CAST_TO_ASCII 6 | TARGET = jom 7 | CONFIG(debug, debug|release) { 8 | TARGET = $$join(TARGET,,,d) 9 | } 10 | 11 | PROJECT_BUILD_ROOT=$$OUT_PWD/../.. 12 | include(../jomlib/use_jomlib.pri) 13 | 14 | contains(QMAKE_CXXFLAGS_RELEASE, -MT) { 15 | QMAKE_LFLAGS_RELEASE += /NODEFAULTLIB:msvcrt 16 | } 17 | 18 | INCLUDEPATH += ../jomlib 19 | HEADERS = application.h 20 | SOURCES = main.cpp application.cpp 21 | RESOURCES = app.qrc 22 | 23 | JOM_VERSION = $$cat(version.txt, lines) 24 | JOM_VERSION = $$first(JOM_VERSION) 25 | JOM_VERSION_MAJOR=$$section(JOM_VERSION, '.', 0, 0) 26 | JOM_VERSION_MINOR=$$section(JOM_VERSION, '.', 1, 1) 27 | JOM_VERSION_PATCH=$$section(JOM_VERSION, '.', 2, 2) 28 | DEFINES += JOM_VERSION_MAJOR=$$JOM_VERSION_MAJOR 29 | DEFINES += JOM_VERSION_MINOR=$$JOM_VERSION_MINOR 30 | DEFINES += JOM_VERSION_PATCH=$$JOM_VERSION_PATCH 31 | 32 | content = $$cat(app.rc.in, blob) 33 | content ~= s/@\([^@]+\)@/\$\${\1}/g 34 | content ~= s/\"/\\\"/g 35 | write_file($$OUT_PWD/jom.rc.in, content) 36 | rcsubst.input = $$OUT_PWD/jom.rc.in 37 | rcsubst.output = $$OUT_PWD/jom.rc 38 | QMAKE_SUBSTITUTES += rcsubst 39 | RC_FILE += $$OUT_PWD/jom.rc 40 | -------------------------------------------------------------------------------- /src/app/app.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | qt.conf 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/app/app.rc.in: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | VS_VERSION_INFO VERSIONINFO 4 | FILEVERSION @JOM_VERSION_MAJOR@,@JOM_VERSION_MINOR@,@JOM_VERSION_PATCH@,0 5 | PRODUCTVERSION @JOM_VERSION_MAJOR@,@JOM_VERSION_MINOR@,@JOM_VERSION_PATCH@,0 6 | FILEFLAGSMASK 0x3fL 7 | #ifdef _DEBUG 8 | FILEFLAGS VS_FF_DEBUG 9 | #else 10 | FILEFLAGS 0x0L 11 | #endif 12 | FILEOS VOS__WINDOWS32 13 | FILETYPE VFT_APP 14 | FILESUBTYPE 0x0L 15 | BEGIN 16 | BLOCK "StringFileInfo" 17 | BEGIN 18 | BLOCK "040904b0" 19 | BEGIN 20 | VALUE "CompanyName", "The Qt Company Ltd.\0" 21 | VALUE "FileDescription", "jom - a parallel make tool\0" 22 | VALUE "FileVersion", "@JOM_VERSION@.0\0" 23 | VALUE "LegalCopyright", "Copyright (C) 2019 The Qt Company Ltd.\0" 24 | VALUE "OriginalFilename", "jom.exe\0" 25 | VALUE "ProductName", "jom\0" 26 | VALUE "ProductVersion", "@JOM_VERSION@.0\0" 27 | END 28 | END 29 | BLOCK "VarFileInfo" 30 | BEGIN 31 | VALUE "Translation", 0x0409, 1200 32 | END 33 | END 34 | /* End of Version info */ 35 | -------------------------------------------------------------------------------- /src/app/application.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of jom. 7 | ** 8 | ** Commercial License Usage 9 | ** Licensees holding valid commercial Qt licenses may use this file in 10 | ** accordance with the commercial license agreement provided with the 11 | ** Software or, alternatively, in accordance with the terms contained in 12 | ** a written agreement between you and The Qt Company. For licensing terms 13 | ** and conditions see https://www.qt.io/terms-conditions. For further 14 | ** information use the contact form at https://www.qt.io/contact-us. 15 | ** 16 | ** GNU General Public License Usage 17 | ** Alternatively, this file may be used under the terms of the GNU 18 | ** General Public License version 3 as published by the Free Software 19 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 20 | ** included in the packaging of this file. Please review the following 21 | ** information to ensure the GNU General Public License requirements will 22 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. 23 | ** 24 | ****************************************************************************/ 25 | 26 | #include "application.h" 27 | #include 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | namespace NMakeFile { 35 | 36 | static bool isSubJOM() 37 | { 38 | return GetEnvironmentVariableA("_JOMSRVKEY_", NULL, 0) > 0; 39 | } 40 | 41 | Application::Application(int &argc, char **argv) 42 | : QCoreApplication(argc, argv) 43 | { 44 | m_bIsSubJOM = NMakeFile::isSubJOM(); 45 | } 46 | 47 | Application::~Application() 48 | { 49 | IoCompletionPort::destroyInstance(); 50 | } 51 | 52 | void Application::exit(int exitCode) 53 | { 54 | QCoreApplication::exit(exitCode); 55 | } 56 | 57 | } // namespace NMakeFile 58 | -------------------------------------------------------------------------------- /src/app/application.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of jom. 7 | ** 8 | ** Commercial License Usage 9 | ** Licensees holding valid commercial Qt licenses may use this file in 10 | ** accordance with the commercial license agreement provided with the 11 | ** Software or, alternatively, in accordance with the terms contained in 12 | ** a written agreement between you and The Qt Company. For licensing terms 13 | ** and conditions see https://www.qt.io/terms-conditions. For further 14 | ** information use the contact form at https://www.qt.io/contact-us. 15 | ** 16 | ** GNU General Public License Usage 17 | ** Alternatively, this file may be used under the terms of the GNU 18 | ** General Public License version 3 as published by the Free Software 19 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 20 | ** included in the packaging of this file. Please review the following 21 | ** information to ensure the GNU General Public License requirements will 22 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. 23 | ** 24 | ****************************************************************************/ 25 | 26 | #ifndef APPLICATION_H 27 | #define APPLICATION_H 28 | 29 | #include 30 | 31 | namespace NMakeFile { 32 | 33 | class Application : public QCoreApplication 34 | { 35 | Q_OBJECT 36 | public: 37 | static Application *instance() { return static_cast(QCoreApplication::instance()); } 38 | 39 | Application(int &argc, char **argv); 40 | ~Application(); 41 | 42 | bool isSubJOM() const { return m_bIsSubJOM; } 43 | 44 | public slots: 45 | void exit(int exitCode); 46 | 47 | private: 48 | bool m_bIsSubJOM; 49 | }; 50 | 51 | } // namespace NMakeFile 52 | 53 | #endif // APPLICATION_H 54 | -------------------------------------------------------------------------------- /src/app/main.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of jom. 7 | ** 8 | ** Commercial License Usage 9 | ** Licensees holding valid commercial Qt licenses may use this file in 10 | ** accordance with the commercial license agreement provided with the 11 | ** Software or, alternatively, in accordance with the terms contained in 12 | ** a written agreement between you and The Qt Company. For licensing terms 13 | ** and conditions see https://www.qt.io/terms-conditions. For further 14 | ** information use the contact form at https://www.qt.io/contact-us. 15 | ** 16 | ** GNU General Public License Usage 17 | ** Alternatively, this file may be used under the terms of the GNU 18 | ** General Public License version 3 as published by the Free Software 19 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 20 | ** included in the packaging of this file. Please review the following 21 | ** information to ensure the GNU General Public License requirements will 22 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. 23 | ** 24 | ****************************************************************************/ 25 | 26 | #include "application.h" 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | #include 43 | #include 44 | 45 | using namespace NMakeFile; 46 | 47 | static void showLogo() 48 | { 49 | fprintf(stderr, "\njom %d.%d.%d - empower your cores\n\n", 50 | JOM_VERSION_MAJOR, JOM_VERSION_MINOR, JOM_VERSION_PATCH); 51 | fflush(stderr); 52 | } 53 | 54 | static void showUsage() 55 | { 56 | printf("Usage: jom @commandfile\n" 57 | " jom [options] [/f makefile] [macro definitions] [targets]\n\n" 58 | "nmake compatible options:\n" 59 | "/A build all targets\n" 60 | "/D display build information\n" 61 | "/E override environment variable macros\n" 62 | "/F use the specified makefile\n" 63 | "/G display included makefiles\n" 64 | "/H show help\n" 65 | "/I ignore all exit codes\n" 66 | "/K keep going - build unrelated targets on error\n" 67 | "/N dry run - just print commands\n" 68 | "/NOLOGO do not print logo\n" 69 | "/P print makefile info\n" 70 | "/R ignore predefined rules and macros\n" 71 | "/S silent mode\n" 72 | "/U print content of inline files\n" 73 | "/L same as /NOLOGO\n" 74 | "/W print the working directory before and after other processing\n" 75 | "/X write stderr to file.\n" 76 | "/Y disable batch mode inference rules\n\n" 77 | "jom only options:\n" 78 | "/DUMPGRAPH show the generated dependency graph\n" 79 | "/DUMPGRAPHDOT dump dependency graph in dot format\n" 80 | "/J use up to n processes in parallel\n" 81 | "/VERSION print version and exit\n"); 82 | } 83 | 84 | static TargetExecutor* g_pTargetExecutor = 0; 85 | 86 | BOOL WINAPI ConsoleCtrlHandlerRoutine(DWORD dwCtrlType) 87 | { 88 | Q_UNUSED(dwCtrlType); 89 | fprintf(stderr, "jom terminated by user (pid=%lld)\n", QCoreApplication::applicationPid()); 90 | fflush(stderr); 91 | 92 | GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); 93 | if (g_pTargetExecutor) 94 | g_pTargetExecutor->removeTempFiles(); 95 | 96 | exit(2); 97 | return TRUE; 98 | } 99 | 100 | QStringList getCommandLineArguments() 101 | { 102 | QStringList commandLineArguments = qApp->arguments().mid(1); 103 | QString makeFlags = qGetEnvironmentVariable(L"JOMFLAGS"); 104 | if (makeFlags.isEmpty()) 105 | makeFlags = qGetEnvironmentVariable(L"MAKEFLAGS"); 106 | if (!makeFlags.isEmpty()) 107 | commandLineArguments.prepend(QLatin1Char('/') + makeFlags); 108 | return commandLineArguments; 109 | } 110 | 111 | static bool initJobServer(const Application &app, ProcessEnvironment *environment, 112 | JobServer **outJobServer) 113 | { 114 | bool mustCreateJobServer = false; 115 | if (app.isSubJOM()) { 116 | int inheritedMaxNumberOfJobs = g_options.maxNumberOfJobs; 117 | const QString str = environment->value(QLatin1String("_JOMJOBCOUNT_")); 118 | if (!str.isEmpty()) { 119 | bool ok; 120 | const int n = str.toInt(&ok); 121 | if (ok && n > 0) 122 | inheritedMaxNumberOfJobs = n; 123 | } 124 | if (g_options.isMaxNumberOfJobsSet 125 | && g_options.maxNumberOfJobs != inheritedMaxNumberOfJobs) 126 | { 127 | fprintf(stderr, "jom: Overriding inherited number of jobs %d with %d. " 128 | "New jobserver created.\n", 129 | inheritedMaxNumberOfJobs, g_options.maxNumberOfJobs); 130 | mustCreateJobServer = true; 131 | } 132 | } else { 133 | mustCreateJobServer = true; 134 | } 135 | 136 | if (mustCreateJobServer) { 137 | JobServer *jobServer = new JobServer(environment); 138 | *outJobServer = jobServer; 139 | if (!jobServer->start(g_options.maxNumberOfJobs)) { 140 | fprintf(stderr, "Cannot start job server: %s.", qPrintable(jobServer->errorString())); 141 | return false; 142 | } 143 | } 144 | return true; 145 | } 146 | 147 | int main(int argc, char* argv[]) 148 | { 149 | int result = 0; 150 | try { 151 | SetConsoleCtrlHandler(&ConsoleCtrlHandlerRoutine, TRUE); 152 | Application app(argc, argv); 153 | QTextCodec::setCodecForLocale(QTextCodec::codecForName("IBM 850")); 154 | MakefileFactory mf; 155 | Options* options = 0; 156 | mf.setEnvironment(QProcess::systemEnvironment()); 157 | if (!mf.apply(getCommandLineArguments(), &options)) { 158 | switch (mf.errorType()) { 159 | case MakefileFactory::CommandLineError: 160 | showUsage(); 161 | return 128; 162 | case MakefileFactory::ParserError: 163 | case MakefileFactory::IOError: 164 | fprintf(stderr, "Error: %s\n", qPrintable(mf.errorString())); 165 | return 2; 166 | } 167 | } 168 | 169 | if (options->showUsageAndExit) { 170 | if (options->showLogo) 171 | showLogo(); 172 | showUsage(); 173 | return 0; 174 | } else if (options->showVersionAndExit) { 175 | printf("jom version %d.%d.%d\n", JOM_VERSION_MAJOR, JOM_VERSION_MINOR, JOM_VERSION_PATCH); 176 | return 0; 177 | } 178 | 179 | if (options->showLogo && !app.isSubJOM()) 180 | showLogo(); 181 | 182 | QScopedPointer mkfile(mf.makefile()); 183 | if (options->displayMakeInformation) { 184 | printf("MACROS:\n\n"); 185 | mkfile->macroTable()->dump(); 186 | printf("\nINFERENCE RULES:\n\n"); 187 | mkfile->dumpInferenceRules(); 188 | printf("\nTARGETS:\n\n"); 189 | mkfile->dumpTargets(); 190 | } 191 | 192 | JobServer *jobServer = 0; 193 | ProcessEnvironment processEnvironment = mkfile->macroTable()->environment(); 194 | if (!initJobServer(app, &processEnvironment, &jobServer)) 195 | return 3; 196 | QScopedPointer jobServerDeleter(jobServer); 197 | 198 | if (options->printWorkingDir) { 199 | printf("jom: Entering directory '%s\n", 200 | qPrintable(QDir::toNativeSeparators(QDir::currentPath()))); 201 | fflush(stdout); 202 | } 203 | 204 | if (mkfile->isParallelExecutionDisabled()) { 205 | printf("jom: parallel job execution disabled for %s\n", qPrintable(mkfile->fileName())); 206 | g_options.maxNumberOfJobs = 1; 207 | } 208 | 209 | TargetExecutor executor(processEnvironment); 210 | QObject::connect(&executor, SIGNAL(finished(int)), &app, SLOT(exit(int))); 211 | g_pTargetExecutor = &executor; 212 | executor.apply(mkfile.data(), mf.activeTargets()); 213 | 214 | QMetaObject::invokeMethod(&executor, "startProcesses", Qt::QueuedConnection); 215 | result = app.exec(); 216 | g_pTargetExecutor = 0; 217 | if (options->printWorkingDir) { 218 | printf("jom: Leaving directory '%s'\n", 219 | qPrintable(QDir::toNativeSeparators(QDir::currentPath()))); 220 | fflush(stdout); 221 | } 222 | } catch (const Exception &e) { 223 | fprintf(stderr, "jom: %s\n", qPrintable(e.message())); 224 | result = 2; 225 | } 226 | return result; 227 | } 228 | -------------------------------------------------------------------------------- /src/app/qt.conf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qt-labs/jom/5b295dbee66504d21b8cc7d05d5e88e87644d586/src/app/qt.conf -------------------------------------------------------------------------------- /src/app/version.txt: -------------------------------------------------------------------------------- 1 | 1.1.4 2 | -------------------------------------------------------------------------------- /src/jomlib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(jomlib STATIC 2 | commandexecutor.cpp 3 | commandexecutor.h 4 | dependencygraph.cpp 5 | dependencygraph.h 6 | exception.cpp 7 | exception.h 8 | fastfileinfo.cpp 9 | fastfileinfo.h 10 | filetime.cpp 11 | filetime.h 12 | helperfunctions.cpp 13 | helperfunctions.h 14 | iocompletionport.cpp 15 | iocompletionport.h 16 | jobclient.cpp 17 | jobclient.h 18 | jobclientacquirehelper.cpp 19 | jobclientacquirehelper.h 20 | jobserver.cpp 21 | jomprocess.cpp 22 | jomprocess.h 23 | macrotable.cpp 24 | macrotable.h 25 | makefile.cpp 26 | makefile.h 27 | makefilefactory.cpp 28 | makefilefactory.h 29 | makefilelinereader.cpp 30 | makefilelinereader.h 31 | options.cpp 32 | options.h 33 | parser.cpp 34 | parser.h 35 | ppexpr_grammar.cpp 36 | ppexpr_grammar_p.h 37 | ppexprparser.cpp 38 | ppexprparser.h 39 | preprocessor.cpp 40 | preprocessor.h 41 | stable.h 42 | targetexecutor.cpp 43 | targetexecutor.h 44 | ) 45 | 46 | target_include_directories(jomlib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) 47 | target_link_libraries(jomlib PUBLIC Qt5::Core) 48 | 49 | # If we're building against a static Qt on Windows, 50 | # we must link manually against all private libraries. 51 | # This should not be necessary. See QTBUG-38913. 52 | get_target_property(qt_core_type Qt5::Core TYPE) 53 | if(qt_core_type MATCHES STATIC_LIBRARY) 54 | target_link_libraries(jomlib PRIVATE mincore userenv winmm ws2_32) 55 | 56 | if(CMAKE_BUILD_TYPE MATCHES Debug) 57 | set(debug_suffix d) 58 | set(qt_build_config DEBUG) 59 | else() 60 | set(debug_suffix "") 61 | set(qt_build_config RELEASE) 62 | endif() 63 | 64 | get_target_property(qt_core_lib_location Qt5::Core IMPORTED_LOCATION_${qt_build_config}) 65 | get_filename_component(qt_core_lib_dir ${qt_core_lib_location} DIRECTORY) 66 | target_link_libraries(jomlib PRIVATE "${qt_core_lib_dir}/qtpcre2${debug_suffix}.lib") 67 | endif() 68 | 69 | target_compile_definitions(jomlib PRIVATE 70 | QT_NO_CAST_FROM_ASCII 71 | QT_NO_CAST_TO_ASCII 72 | UNICODE 73 | ) 74 | 75 | if(MSVC) 76 | target_compile_definitions(jomlib PUBLIC 77 | _CRT_SECURE_NO_WARNINGS 78 | ) 79 | endif() 80 | -------------------------------------------------------------------------------- /src/jomlib/commandexecutor.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of jom. 7 | ** 8 | ** Commercial License Usage 9 | ** Licensees holding valid commercial Qt licenses may use this file in 10 | ** accordance with the commercial license agreement provided with the 11 | ** Software or, alternatively, in accordance with the terms contained in 12 | ** a written agreement between you and The Qt Company. For licensing terms 13 | ** and conditions see https://www.qt.io/terms-conditions. For further 14 | ** information use the contact form at https://www.qt.io/contact-us. 15 | ** 16 | ** GNU General Public License Usage 17 | ** Alternatively, this file may be used under the terms of the GNU 18 | ** General Public License version 3 as published by the Free Software 19 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 20 | ** included in the packaging of this file. Please review the following 21 | ** information to ensure the GNU General Public License requirements will 22 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. 23 | ** 24 | ****************************************************************************/ 25 | 26 | #ifndef COMMANDEXECUTOR_H 27 | #define COMMANDEXECUTOR_H 28 | 29 | #include "makefile.h" 30 | #include "jomprocess.h" 31 | #include 32 | #include 33 | 34 | QT_BEGIN_NAMESPACE 35 | class QStringList; 36 | QT_END_NAMESPACE 37 | 38 | namespace NMakeFile { 39 | 40 | class CommandExecutor : public QObject 41 | { 42 | Q_OBJECT 43 | public: 44 | CommandExecutor(QObject* parent, const ProcessEnvironment &environment); 45 | ~CommandExecutor(); 46 | 47 | void start(DescriptionBlock* target); 48 | DescriptionBlock* target() { return m_pTarget; } 49 | bool isActive() const { return m_active; } 50 | void waitForFinished(); 51 | void cleanupTempFiles(); 52 | void setBufferedOutput(bool b) { m_process.setBufferedOutput(b); } 53 | bool isBufferedOutputSet() const { return m_process.isBufferedOutputSet(); } 54 | 55 | public slots: 56 | void setEnvironment(const ProcessEnvironment &environment); 57 | 58 | signals: 59 | void environmentChanged(const ProcessEnvironment &environment); 60 | void finished(CommandExecutor* process, bool abortMakeProcess); 61 | 62 | private slots: 63 | void onProcessError(Process::ProcessError error); 64 | void onProcessFinished(int exitCode, Process::ExitStatus exitStatus); 65 | 66 | private: 67 | void finishExecution(bool commandFailed); 68 | void executeCurrentCommandLine(); 69 | void createTempFiles(); 70 | void writeToChannel(const QByteArray& data, FILE *channel); 71 | void writeToStandardOutput(const QByteArray& data); 72 | void writeToStandardError(const QByteArray& data); 73 | bool isSimpleCommandLine(const QString &cmdLine); 74 | bool exec_cd(const QString &commandLine); 75 | 76 | private: 77 | static ulong m_startUpTickCount; 78 | static QString m_tempPath; 79 | Process m_process; 80 | DescriptionBlock* m_pTarget; 81 | 82 | struct TempFile 83 | { 84 | QFile* file; 85 | bool keep; 86 | }; 87 | 88 | QList m_tempFiles; 89 | int m_currentCommandIdx; 90 | QString m_nextWorkingDir; 91 | bool m_ignoreProcessErrors; 92 | bool m_active; 93 | }; 94 | 95 | } // namespace NMakeFile 96 | 97 | #endif // COMMANDEXECUTOR_H 98 | -------------------------------------------------------------------------------- /src/jomlib/dependencygraph.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of jom. 7 | ** 8 | ** Commercial License Usage 9 | ** Licensees holding valid commercial Qt licenses may use this file in 10 | ** accordance with the commercial license agreement provided with the 11 | ** Software or, alternatively, in accordance with the terms contained in 12 | ** a written agreement between you and The Qt Company. For licensing terms 13 | ** and conditions see https://www.qt.io/terms-conditions. For further 14 | ** information use the contact form at https://www.qt.io/contact-us. 15 | ** 16 | ** GNU General Public License Usage 17 | ** Alternatively, this file may be used under the terms of the GNU 18 | ** General Public License version 3 as published by the Free Software 19 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 20 | ** included in the packaging of this file. Please review the following 21 | ** information to ensure the GNU General Public License requirements will 22 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. 23 | ** 24 | ****************************************************************************/ 25 | 26 | #ifndef DEPENDENCYGRAPH_H 27 | #define DEPENDENCYGRAPH_H 28 | 29 | #include 30 | #include 31 | 32 | namespace NMakeFile { 33 | 34 | class DescriptionBlock; 35 | 36 | class DependencyGraph 37 | { 38 | public: 39 | DependencyGraph(); 40 | ~DependencyGraph(); 41 | 42 | void build(DescriptionBlock* target); 43 | void markParentsRecursivlyUnbuildable(DescriptionBlock *target); 44 | bool isUnbuildable(DescriptionBlock *target) const; 45 | bool isEmpty() const; 46 | void removeLeaf(DescriptionBlock* target); 47 | DescriptionBlock *findAvailableTarget(bool ignoreTimeStamps); 48 | void dump(); 49 | void dotDump(); 50 | void clear(); 51 | 52 | private: 53 | bool isTargetUpToDate(DescriptionBlock* target); 54 | 55 | struct Node 56 | { 57 | enum State {UnknownState, ExecutingState, Unbuildable}; 58 | 59 | State state; 60 | DescriptionBlock* target; 61 | QList children; 62 | QList parents; 63 | }; 64 | 65 | Node* createNode(DescriptionBlock* target, Node* parent); 66 | void deleteNode(Node* node); 67 | void removeLeaf(Node* node); 68 | void internalBuild(Node *node, QSet &seen); 69 | void addEdge(Node* parent, Node* child); 70 | void internalDump(Node* node, QString& indent); 71 | void internalDotDump(Node* node, const QString& parent); 72 | void displayNodeBuildInfo(Node* node, bool isUpToDate); 73 | static void markParentsRecursivlyUnbuildable(Node *node); 74 | 75 | private: 76 | Node* m_root; 77 | QHash m_nodeContainer; 78 | QList m_leaves; 79 | bool m_bDirtyLeaves; 80 | }; 81 | 82 | } // namespace NMakeFile 83 | 84 | #endif // DEPENDENCYGRAPH_H 85 | -------------------------------------------------------------------------------- /src/jomlib/exception.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of jom. 7 | ** 8 | ** Commercial License Usage 9 | ** Licensees holding valid commercial Qt licenses may use this file in 10 | ** accordance with the commercial license agreement provided with the 11 | ** Software or, alternatively, in accordance with the terms contained in 12 | ** a written agreement between you and The Qt Company. For licensing terms 13 | ** and conditions see https://www.qt.io/terms-conditions. For further 14 | ** information use the contact form at https://www.qt.io/contact-us. 15 | ** 16 | ** GNU General Public License Usage 17 | ** Alternatively, this file may be used under the terms of the GNU 18 | ** General Public License version 3 as published by the Free Software 19 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 20 | ** included in the packaging of this file. Please review the following 21 | ** information to ensure the GNU General Public License requirements will 22 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. 23 | ** 24 | ****************************************************************************/ 25 | 26 | #include "exception.h" 27 | 28 | #include 29 | 30 | namespace NMakeFile { 31 | 32 | Exception::Exception(const QString& message) 33 | : m_message(message) 34 | { 35 | } 36 | 37 | const QString Exception::toString() const 38 | { 39 | return m_message; 40 | } 41 | 42 | FileException::FileException(const QString& message, const QString& fileName, int line) 43 | : Exception(message), 44 | m_fileName(fileName), 45 | m_line(line) 46 | { 47 | } 48 | 49 | const QString FileException::toString() const 50 | { 51 | QString output = m_message; 52 | if (!m_fileName.isEmpty()) { 53 | output += QLatin1String(" in "); 54 | QString fileName = m_fileName; 55 | fileName = QDir::toNativeSeparators(fileName); 56 | output += fileName; 57 | output += QLatin1String(" "); 58 | } 59 | 60 | if (m_line > 0) { 61 | output += QString::fromLatin1("line %1").arg(m_line); 62 | } 63 | 64 | return output; 65 | } 66 | 67 | } // namespace NMakeFile 68 | -------------------------------------------------------------------------------- /src/jomlib/exception.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of jom. 7 | ** 8 | ** Commercial License Usage 9 | ** Licensees holding valid commercial Qt licenses may use this file in 10 | ** accordance with the commercial license agreement provided with the 11 | ** Software or, alternatively, in accordance with the terms contained in 12 | ** a written agreement between you and The Qt Company. For licensing terms 13 | ** and conditions see https://www.qt.io/terms-conditions. For further 14 | ** information use the contact form at https://www.qt.io/contact-us. 15 | ** 16 | ** GNU General Public License Usage 17 | ** Alternatively, this file may be used under the terms of the GNU 18 | ** General Public License version 3 as published by the Free Software 19 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 20 | ** included in the packaging of this file. Please review the following 21 | ** information to ensure the GNU General Public License requirements will 22 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. 23 | ** 24 | ****************************************************************************/ 25 | 26 | #ifndef EXCEPTION_H 27 | #define EXCEPTION_H 28 | 29 | #include 30 | 31 | namespace NMakeFile { 32 | 33 | class Exception 34 | { 35 | public: 36 | explicit Exception(const QString& message = QString()); 37 | 38 | const QString& message() const { return m_message; } 39 | virtual const QString toString() const; 40 | 41 | protected: 42 | QString m_message; 43 | }; 44 | 45 | class FileException : public Exception 46 | { 47 | public: 48 | explicit FileException(const QString& message = QString(), const QString& fileName = QString(), int line = 0); 49 | 50 | const QString& fileName() const { return m_fileName; } 51 | int line() const { return m_line; } 52 | const QString toString() const; 53 | 54 | protected: 55 | QString m_fileName; 56 | int m_line; 57 | }; 58 | 59 | } // namespace NMakeFile 60 | 61 | #endif // EXCEPTION_H 62 | -------------------------------------------------------------------------------- /src/jomlib/fastfileinfo.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of jom. 7 | ** 8 | ** Commercial License Usage 9 | ** Licensees holding valid commercial Qt licenses may use this file in 10 | ** accordance with the commercial license agreement provided with the 11 | ** Software or, alternatively, in accordance with the terms contained in 12 | ** a written agreement between you and The Qt Company. For licensing terms 13 | ** and conditions see https://www.qt.io/terms-conditions. For further 14 | ** information use the contact form at https://www.qt.io/contact-us. 15 | ** 16 | ** GNU General Public License Usage 17 | ** Alternatively, this file may be used under the terms of the GNU 18 | ** General Public License version 3 as published by the Free Software 19 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 20 | ** included in the packaging of this file. Please review the following 21 | ** information to ensure the GNU General Public License requirements will 22 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. 23 | ** 24 | ****************************************************************************/ 25 | 26 | #include "fastfileinfo.h" 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | namespace NMakeFile { 34 | 35 | static_assert(sizeof(FastFileInfo::InternalType) == sizeof(WIN32_FILE_ATTRIBUTE_DATA), 36 | "FastFileInfo::InternalType has wrong size"); 37 | 38 | inline WIN32_FILE_ATTRIBUTE_DATA* z(FastFileInfo::InternalType &internalData) 39 | { 40 | return reinterpret_cast(&internalData); 41 | } 42 | 43 | inline const WIN32_FILE_ATTRIBUTE_DATA* z(const FastFileInfo::InternalType &internalData) 44 | { 45 | return reinterpret_cast(&internalData); 46 | } 47 | 48 | static WIN32_FILE_ATTRIBUTE_DATA createInvalidFAD() 49 | { 50 | WIN32_FILE_ATTRIBUTE_DATA fad = {0}; 51 | fad.dwFileAttributes = INVALID_FILE_ATTRIBUTES; 52 | return fad; 53 | } 54 | 55 | static QHash fadHash; 56 | 57 | FastFileInfo::FastFileInfo(const QString &fileName) 58 | { 59 | static const WIN32_FILE_ATTRIBUTE_DATA invalidFAD = createInvalidFAD(); 60 | *z(m_attributes) = fadHash.value(fileName, invalidFAD); 61 | if (z(m_attributes)->dwFileAttributes != INVALID_FILE_ATTRIBUTES) 62 | return; 63 | 64 | static const QString longPathPrefix = QStringLiteral("\\\\?\\"); 65 | QString nativeFilePath = QDir::toNativeSeparators(QFileInfo(fileName).absoluteFilePath()); 66 | if (!nativeFilePath.startsWith(longPathPrefix)) 67 | nativeFilePath.prepend(longPathPrefix); 68 | 69 | if (!GetFileAttributesEx(reinterpret_cast(nativeFilePath.utf16()), 70 | GetFileExInfoStandard, &m_attributes)) 71 | { 72 | z(m_attributes)->dwFileAttributes = INVALID_FILE_ATTRIBUTES; 73 | return; 74 | } 75 | 76 | fadHash.insert(fileName, *z(m_attributes)); 77 | } 78 | 79 | bool FastFileInfo::exists() const 80 | { 81 | return z(m_attributes)->dwFileAttributes != INVALID_FILE_ATTRIBUTES; 82 | } 83 | 84 | FileTime FastFileInfo::lastModified() const 85 | { 86 | const WIN32_FILE_ATTRIBUTE_DATA *fattr = z(m_attributes); 87 | if (fattr->dwFileAttributes == INVALID_FILE_ATTRIBUTES) { 88 | return FileTime(); 89 | } else { 90 | return FileTime(reinterpret_cast(fattr->ftLastWriteTime)); 91 | } 92 | } 93 | 94 | void FastFileInfo::clearCacheForFile(const QString &fileName) 95 | { 96 | fadHash.remove(fileName); 97 | } 98 | 99 | } // NMakeFile 100 | -------------------------------------------------------------------------------- /src/jomlib/fastfileinfo.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of jom. 7 | ** 8 | ** Commercial License Usage 9 | ** Licensees holding valid commercial Qt licenses may use this file in 10 | ** accordance with the commercial license agreement provided with the 11 | ** Software or, alternatively, in accordance with the terms contained in 12 | ** a written agreement between you and The Qt Company. For licensing terms 13 | ** and conditions see https://www.qt.io/terms-conditions. For further 14 | ** information use the contact form at https://www.qt.io/contact-us. 15 | ** 16 | ** GNU General Public License Usage 17 | ** Alternatively, this file may be used under the terms of the GNU 18 | ** General Public License version 3 as published by the Free Software 19 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 20 | ** included in the packaging of this file. Please review the following 21 | ** information to ensure the GNU General Public License requirements will 22 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. 23 | ** 24 | ****************************************************************************/ 25 | 26 | #ifndef FASTFILEINFO_H 27 | #define FASTFILEINFO_H 28 | 29 | #include "filetime.h" 30 | 31 | namespace NMakeFile { 32 | 33 | class FastFileInfo 34 | { 35 | public: 36 | FastFileInfo(const QString &fileName); 37 | 38 | bool exists() const; 39 | FileTime lastModified() const; 40 | 41 | static void clearCacheForFile(const QString &fileName); 42 | 43 | struct InternalType 44 | { 45 | quint8 z[36]; 46 | }; 47 | 48 | private: 49 | InternalType m_attributes; 50 | }; 51 | 52 | } // NMakeFile 53 | 54 | #endif // FASTFILEINFO_H 55 | -------------------------------------------------------------------------------- /src/jomlib/filetime.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of jom. 7 | ** 8 | ** Commercial License Usage 9 | ** Licensees holding valid commercial Qt licenses may use this file in 10 | ** accordance with the commercial license agreement provided with the 11 | ** Software or, alternatively, in accordance with the terms contained in 12 | ** a written agreement between you and The Qt Company. For licensing terms 13 | ** and conditions see https://www.qt.io/terms-conditions. For further 14 | ** information use the contact form at https://www.qt.io/contact-us. 15 | ** 16 | ** GNU General Public License Usage 17 | ** Alternatively, this file may be used under the terms of the GNU 18 | ** General Public License version 3 as published by the Free Software 19 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 20 | ** included in the packaging of this file. Please review the following 21 | ** information to ensure the GNU General Public License requirements will 22 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. 23 | ** 24 | ****************************************************************************/ 25 | 26 | #include "filetime.h" 27 | #include 28 | #include 29 | 30 | namespace NMakeFile { 31 | 32 | static_assert(sizeof(FileTime::InternalType) == sizeof(FILETIME), 33 | "FileTime::InternalType has wrong size"); 34 | 35 | FileTime::FileTime() 36 | : m_fileTime(0) 37 | { 38 | } 39 | 40 | bool FileTime::operator < (const FileTime &rhs) const 41 | { 42 | const FILETIME *const t1 = reinterpret_cast(&m_fileTime); 43 | const FILETIME *const t2 = reinterpret_cast(&rhs.m_fileTime); 44 | return CompareFileTime(t1, t2) < 0; 45 | } 46 | 47 | void FileTime::clear() 48 | { 49 | m_fileTime = 0; 50 | } 51 | 52 | bool FileTime::isValid() const 53 | { 54 | return m_fileTime != 0; 55 | } 56 | 57 | FileTime FileTime::currentTime() 58 | { 59 | FileTime result; 60 | SYSTEMTIME st; 61 | GetSystemTime(&st); 62 | FILETIME *const ft = reinterpret_cast(&result.m_fileTime); 63 | SystemTimeToFileTime(&st, ft); 64 | return result; 65 | } 66 | 67 | QString FileTime::toString() const 68 | { 69 | const FILETIME *const ft = reinterpret_cast(&m_fileTime); 70 | SYSTEMTIME stUTC, stLocal; 71 | FileTimeToSystemTime(ft, &stUTC); 72 | SystemTimeToTzSpecificLocalTime(NULL, &stUTC, &stLocal); 73 | WCHAR szString[512]; 74 | HRESULT hr = StringCchPrintf(szString, sizeof(szString) / sizeof(WCHAR), 75 | L"%02d.%02d.%d %02d:%02d:%02d", 76 | stLocal.wDay, stLocal.wMonth, stLocal.wYear, 77 | stLocal.wHour, stLocal.wMinute, stLocal.wSecond); 78 | return SUCCEEDED(hr) ? QString::fromUtf16((unsigned short*)szString) : QString(); 79 | } 80 | 81 | } // namespace NMakeFile 82 | -------------------------------------------------------------------------------- /src/jomlib/filetime.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of jom. 7 | ** 8 | ** Commercial License Usage 9 | ** Licensees holding valid commercial Qt licenses may use this file in 10 | ** accordance with the commercial license agreement provided with the 11 | ** Software or, alternatively, in accordance with the terms contained in 12 | ** a written agreement between you and The Qt Company. For licensing terms 13 | ** and conditions see https://www.qt.io/terms-conditions. For further 14 | ** information use the contact form at https://www.qt.io/contact-us. 15 | ** 16 | ** GNU General Public License Usage 17 | ** Alternatively, this file may be used under the terms of the GNU 18 | ** General Public License version 3 as published by the Free Software 19 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 20 | ** included in the packaging of this file. Please review the following 21 | ** information to ensure the GNU General Public License requirements will 22 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. 23 | ** 24 | ****************************************************************************/ 25 | 26 | #ifndef FILETIME_H 27 | #define FILETIME_H 28 | 29 | #include 30 | #include 31 | 32 | namespace NMakeFile { 33 | 34 | class FileTime 35 | { 36 | public: 37 | FileTime(); 38 | 39 | typedef quint64 InternalType; 40 | 41 | FileTime(const InternalType &ft) 42 | : m_fileTime(ft) 43 | { } 44 | 45 | bool operator < (const FileTime &rhs) const; 46 | bool operator <= (const FileTime &rhs) const 47 | { 48 | return operator < (rhs) || operator == (rhs); 49 | } 50 | bool operator == (const FileTime &rhs) const 51 | { 52 | return m_fileTime == rhs.m_fileTime; 53 | } 54 | 55 | void clear(); 56 | bool isValid() const; 57 | QString toString() const; 58 | InternalType internalRepresentation() const { return m_fileTime; } 59 | 60 | static FileTime currentTime(); 61 | 62 | private: 63 | friend class FastFileInfo; 64 | InternalType m_fileTime; 65 | }; 66 | 67 | } // namespace NMakeFile 68 | 69 | #endif // FILETIME_H 70 | -------------------------------------------------------------------------------- /src/jomlib/helperfunctions.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of jom. 7 | ** 8 | ** Commercial License Usage 9 | ** Licensees holding valid commercial Qt licenses may use this file in 10 | ** accordance with the commercial license agreement provided with the 11 | ** Software or, alternatively, in accordance with the terms contained in 12 | ** a written agreement between you and The Qt Company. For licensing terms 13 | ** and conditions see https://www.qt.io/terms-conditions. For further 14 | ** information use the contact form at https://www.qt.io/contact-us. 15 | ** 16 | ** GNU General Public License Usage 17 | ** Alternatively, this file may be used under the terms of the GNU 18 | ** General Public License version 3 as published by the Free Software 19 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 20 | ** included in the packaging of this file. Please review the following 21 | ** information to ensure the GNU General Public License requirements will 22 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. 23 | ** 24 | ****************************************************************************/ 25 | 26 | #include "helperfunctions.h" 27 | #include 28 | 29 | /** 30 | * Splits the string, respects "foo bar" and "foo ""knuffi"" bar". 31 | */ 32 | QStringList splitCommandLine(QString commandLine) 33 | { 34 | QString str; 35 | QStringList arguments; 36 | commandLine.append(QLatin1Char(' ')); // append artificial space 37 | bool escapedQuote = false; 38 | bool insideQuotes = false; 39 | for (int i=0; i < commandLine.count(); ++i) { 40 | if (commandLine.at(i).isSpace() && !insideQuotes) { 41 | escapedQuote = false; 42 | str = str.trimmed(); 43 | if (!str.isEmpty()) { 44 | arguments.append(str); 45 | str.clear(); 46 | } 47 | } else { 48 | if (commandLine.at(i) == QLatin1Char('"')) { 49 | if (escapedQuote) { 50 | str.append(QLatin1Char('"')); 51 | escapedQuote = false; 52 | } else { 53 | escapedQuote = true; 54 | } 55 | insideQuotes = !insideQuotes; 56 | } else { 57 | str.append(commandLine.at(i)); 58 | escapedQuote = false; 59 | } 60 | } 61 | } 62 | return arguments; 63 | } 64 | 65 | QString trimLeft(const QString &s) 66 | { 67 | QString result = s; 68 | while (!result.isEmpty() && result[0].isSpace()) 69 | result.remove(0, 1); 70 | return result; 71 | } 72 | 73 | QString qGetEnvironmentVariable(const wchar_t *lpName) 74 | { 75 | const size_t bufferSize = 32767; 76 | TCHAR buffer[bufferSize]; 77 | if (GetEnvironmentVariable(lpName, buffer, bufferSize)) 78 | return QString::fromWCharArray(buffer); 79 | return QString(); 80 | } 81 | 82 | bool qSetEnvironmentVariable(const QString &name, const QString &value) 83 | { 84 | return SetEnvironmentVariable( 85 | reinterpret_cast(name.utf16()), 86 | reinterpret_cast(value.utf16())); 87 | } 88 | -------------------------------------------------------------------------------- /src/jomlib/helperfunctions.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of jom. 7 | ** 8 | ** Commercial License Usage 9 | ** Licensees holding valid commercial Qt licenses may use this file in 10 | ** accordance with the commercial license agreement provided with the 11 | ** Software or, alternatively, in accordance with the terms contained in 12 | ** a written agreement between you and The Qt Company. For licensing terms 13 | ** and conditions see https://www.qt.io/terms-conditions. For further 14 | ** information use the contact form at https://www.qt.io/contact-us. 15 | ** 16 | ** GNU General Public License Usage 17 | ** Alternatively, this file may be used under the terms of the GNU 18 | ** General Public License version 3 as published by the Free Software 19 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 20 | ** included in the packaging of this file. Please review the following 21 | ** information to ensure the GNU General Public License requirements will 22 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. 23 | ** 24 | ****************************************************************************/ 25 | 26 | #ifndef HELPERFUNCTIONS_H 27 | #define HELPERFUNCTIONS_H 28 | 29 | #include 30 | #include 31 | 32 | inline QString fileNameFromFilePath(const QString& filePath) 33 | { 34 | int idx = qMax(filePath.lastIndexOf(QLatin1Char('/')), filePath.lastIndexOf(QLatin1Char('\\'))); 35 | if (idx == -1) 36 | return filePath; 37 | QString fileName = filePath; 38 | fileName.remove(0, idx+1); 39 | return fileName; 40 | } 41 | 42 | inline bool isSpaceOrTab(const QChar& ch) 43 | { 44 | return ch == QLatin1Char(' ') || ch == QLatin1Char('\t'); 45 | } 46 | 47 | inline bool startsWithSpaceOrTab(const QString& str) 48 | { 49 | Q_ASSERT(!str.isEmpty()); 50 | return isSpaceOrTab(str.at(0)); 51 | } 52 | 53 | inline void removeDirSeparatorAtEnd(QString& directory) 54 | { 55 | if (directory.endsWith(QLatin1Char('/')) || directory.endsWith(QLatin1Char('\\'))) 56 | directory.chop(1); 57 | } 58 | 59 | inline void removeDoubleQuotes(QString& targetName) 60 | { 61 | const QChar dblQuote = QLatin1Char('"'); 62 | if (targetName.startsWith(dblQuote) && targetName.endsWith(dblQuote)) { 63 | targetName.chop(1); 64 | targetName.remove(0, 1); 65 | } 66 | } 67 | 68 | /** 69 | * Splits the string, respects "foo bar" and "foo ""knuffi"" bar". 70 | */ 71 | QStringList splitCommandLine(QString commandLine); 72 | 73 | /** 74 | * Returns a copy of s with all whitespace removed from the left. 75 | */ 76 | QString trimLeft(const QString &s); 77 | 78 | QString qGetEnvironmentVariable(const wchar_t *lpName); 79 | bool qSetEnvironmentVariable(const QString &name, const QString &value); 80 | 81 | #endif // HELPERFUNCTIONS_H 82 | -------------------------------------------------------------------------------- /src/jomlib/iocompletionport.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of jom. 7 | ** 8 | ** Commercial License Usage 9 | ** Licensees holding valid commercial Qt licenses may use this file in 10 | ** accordance with the commercial license agreement provided with the 11 | ** Software or, alternatively, in accordance with the terms contained in 12 | ** a written agreement between you and The Qt Company. For licensing terms 13 | ** and conditions see https://www.qt.io/terms-conditions. For further 14 | ** information use the contact form at https://www.qt.io/contact-us. 15 | ** 16 | ** GNU General Public License Usage 17 | ** Alternatively, this file may be used under the terms of the GNU 18 | ** General Public License version 3 as published by the Free Software 19 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 20 | ** included in the packaging of this file. Please review the following 21 | ** information to ensure the GNU General Public License requirements will 22 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. 23 | ** 24 | ****************************************************************************/ 25 | 26 | #include "iocompletionport.h" 27 | 28 | namespace NMakeFile { 29 | 30 | IoCompletionPort *IoCompletionPort::m_instance = 0; 31 | 32 | IoCompletionPort::IoCompletionPort() 33 | : hPort(INVALID_HANDLE_VALUE) 34 | { 35 | setObjectName(QLatin1String("I/O completion port thread")); 36 | HANDLE hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, NULL, 0); 37 | if (!hIOCP) { 38 | qWarning("CreateIoCompletionPort failed with error code %d.\n", GetLastError()); 39 | return; 40 | } 41 | hPort = hIOCP; 42 | } 43 | 44 | IoCompletionPort::~IoCompletionPort() 45 | { 46 | PostQueuedCompletionStatus(hPort, 0, NULL, NULL); 47 | QThread::wait(); 48 | CloseHandle(hPort); 49 | } 50 | 51 | IoCompletionPort *IoCompletionPort::instance() 52 | { 53 | if (!m_instance) 54 | m_instance = new IoCompletionPort; 55 | return m_instance; 56 | } 57 | 58 | void IoCompletionPort::destroyInstance() 59 | { 60 | delete m_instance; 61 | m_instance = 0; 62 | } 63 | 64 | void IoCompletionPort::registerObserver(IoCompletionPortObserver *observer, HANDLE hFile) 65 | { 66 | HANDLE hIOCP = CreateIoCompletionPort(hFile, hPort, reinterpret_cast(observer), 0); 67 | if (!hIOCP) { 68 | qWarning("Can't associate file handle with I/O completion port. Error code %d.\n", GetLastError()); 69 | return; 70 | } 71 | mutex.lock(); 72 | observers.insert(observer); 73 | mutex.unlock(); 74 | if (!QThread::isRunning()) 75 | QThread::start(); 76 | } 77 | 78 | void IoCompletionPort::unregisterObserver(IoCompletionPortObserver *observer) 79 | { 80 | mutex.lock(); 81 | observers.remove(observer); 82 | mutex.unlock(); 83 | } 84 | 85 | void IoCompletionPort::run() 86 | { 87 | DWORD dwBytesRead; 88 | ULONG_PTR pulCompletionKey; 89 | OVERLAPPED *overlapped; 90 | 91 | for (;;) { 92 | BOOL success = GetQueuedCompletionStatus(hPort, 93 | &dwBytesRead, 94 | &pulCompletionKey, 95 | &overlapped, 96 | INFINITE); 97 | 98 | DWORD errorCode = success ? ERROR_SUCCESS : GetLastError(); 99 | if (!success && !overlapped) { 100 | printf("GetQueuedCompletionStatus failed with error code %d.\n", errorCode); 101 | return; 102 | } 103 | 104 | if (success && !(dwBytesRead || pulCompletionKey || overlapped)) { 105 | // We've posted null values via PostQueuedCompletionStatus to end this thread. 106 | return; 107 | } 108 | 109 | IoCompletionPortObserver *observer = reinterpret_cast(pulCompletionKey); 110 | mutex.lock(); 111 | if (observers.contains(observer)) 112 | observer->completionPortNotified(dwBytesRead, errorCode); 113 | mutex.unlock(); 114 | } 115 | } 116 | 117 | } // namespace NMakeFile 118 | -------------------------------------------------------------------------------- /src/jomlib/iocompletionport.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of jom. 7 | ** 8 | ** Commercial License Usage 9 | ** Licensees holding valid commercial Qt licenses may use this file in 10 | ** accordance with the commercial license agreement provided with the 11 | ** Software or, alternatively, in accordance with the terms contained in 12 | ** a written agreement between you and The Qt Company. For licensing terms 13 | ** and conditions see https://www.qt.io/terms-conditions. For further 14 | ** information use the contact form at https://www.qt.io/contact-us. 15 | ** 16 | ** GNU General Public License Usage 17 | ** Alternatively, this file may be used under the terms of the GNU 18 | ** General Public License version 3 as published by the Free Software 19 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 20 | ** included in the packaging of this file. Please review the following 21 | ** information to ensure the GNU General Public License requirements will 22 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. 23 | ** 24 | ****************************************************************************/ 25 | 26 | #ifndef IOCOMPLETIONPORT_H 27 | #define IOCOMPLETIONPORT_H 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | namespace NMakeFile { 35 | 36 | class IoCompletionPortObserver 37 | { 38 | public: 39 | virtual void completionPortNotified(DWORD numberOfBytes, DWORD errorCode) = 0; 40 | }; 41 | 42 | class IoCompletionPort : protected QThread 43 | { 44 | public: 45 | static IoCompletionPort *instance(); 46 | static void destroyInstance(); 47 | 48 | void registerObserver(IoCompletionPortObserver *notifier, HANDLE hFile); 49 | void unregisterObserver(IoCompletionPortObserver *notifier); 50 | 51 | protected: 52 | void run(); 53 | 54 | private: 55 | IoCompletionPort(); 56 | ~IoCompletionPort(); 57 | 58 | static IoCompletionPort *m_instance; 59 | HANDLE hPort; 60 | QSet observers; 61 | QMutex mutex; 62 | }; 63 | 64 | } // namespace NMakeFile 65 | 66 | #endif // IOCOMPLETIONPORT_H 67 | -------------------------------------------------------------------------------- /src/jomlib/jobclient.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of jom. 7 | ** 8 | ** Commercial License Usage 9 | ** Licensees holding valid commercial Qt licenses may use this file in 10 | ** accordance with the commercial license agreement provided with the 11 | ** Software or, alternatively, in accordance with the terms contained in 12 | ** a written agreement between you and The Qt Company. For licensing terms 13 | ** and conditions see https://www.qt.io/terms-conditions. For further 14 | ** information use the contact form at https://www.qt.io/contact-us. 15 | ** 16 | ** GNU General Public License Usage 17 | ** Alternatively, this file may be used under the terms of the GNU 18 | ** General Public License version 3 as published by the Free Software 19 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 20 | ** included in the packaging of this file. Please review the following 21 | ** information to ensure the GNU General Public License requirements will 22 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. 23 | ** 24 | ****************************************************************************/ 25 | 26 | #include "jobclient.h" 27 | #include "jobclientacquirehelper.h" 28 | #include "helperfunctions.h" 29 | 30 | #include 31 | #include 32 | 33 | namespace NMakeFile { 34 | 35 | JobClient::JobClient(ProcessEnvironment *environment, QObject *parent) 36 | : QObject(parent) 37 | , m_environment(environment) 38 | , m_semaphore(0) 39 | , m_acquireThread(new QThread(this)) 40 | , m_acquireHelper(0) 41 | , m_isAcquiring(false) 42 | { 43 | } 44 | 45 | JobClient::~JobClient() 46 | { 47 | if (isAcquiring()) 48 | qWarning("JobClient destroyed while still acquiring."); 49 | m_acquireThread->quit(); 50 | m_acquireThread->wait(2500); 51 | delete m_acquireHelper; 52 | delete m_semaphore; 53 | } 54 | 55 | bool JobClient::start() 56 | { 57 | Q_ASSERT(!m_semaphore && !m_acquireHelper); 58 | Q_ASSERT(!m_acquireThread->isRunning()); 59 | 60 | const QString semaphoreKey = m_environment->value(QLatin1String("_JOMSRVKEY_")); 61 | if (semaphoreKey.isEmpty()) { 62 | setError(QLatin1String("Cannot determine jobserver name.")); 63 | return false; 64 | } 65 | m_semaphore = new QSystemSemaphore(semaphoreKey); 66 | if (m_semaphore->error() != QSystemSemaphore::NoError) { 67 | setError(m_semaphore->errorString()); 68 | return false; 69 | } 70 | 71 | m_acquireHelper = new JobClientAcquireHelper(m_semaphore); 72 | m_acquireHelper->moveToThread(m_acquireThread); 73 | connect(this, &JobClient::startAcquisition, m_acquireHelper, &JobClientAcquireHelper::acquire); 74 | connect(m_acquireHelper, &JobClientAcquireHelper::acquired, this, &JobClient::onHelperAcquired); 75 | m_acquireThread->start(); 76 | return true; 77 | } 78 | 79 | void JobClient::asyncAcquire() 80 | { 81 | Q_ASSERT(m_semaphore); 82 | Q_ASSERT(m_acquireHelper); 83 | Q_ASSERT(m_acquireThread->isRunning()); 84 | 85 | m_isAcquiring = true; 86 | emit startAcquisition(); 87 | } 88 | 89 | void JobClient::onHelperAcquired() 90 | { 91 | m_isAcquiring = false; 92 | emit acquired(); 93 | } 94 | 95 | bool JobClient::isAcquiring() const 96 | { 97 | return m_isAcquiring; 98 | } 99 | 100 | void JobClient::release() 101 | { 102 | Q_ASSERT(m_semaphore); 103 | 104 | if (!m_semaphore->release()) 105 | qWarning("QSystemSemaphore::release failed: %s (%d)", 106 | qPrintable(m_semaphore->errorString()), m_semaphore->error()); 107 | } 108 | 109 | QString JobClient::errorString() const 110 | { 111 | return m_errorString; 112 | } 113 | 114 | void JobClient::setError(const QString &errorMessage) 115 | { 116 | m_errorString = errorMessage; 117 | } 118 | 119 | } // namespace NMakeFile 120 | -------------------------------------------------------------------------------- /src/jomlib/jobclient.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of jom. 7 | ** 8 | ** Commercial License Usage 9 | ** Licensees holding valid commercial Qt licenses may use this file in 10 | ** accordance with the commercial license agreement provided with the 11 | ** Software or, alternatively, in accordance with the terms contained in 12 | ** a written agreement between you and The Qt Company. For licensing terms 13 | ** and conditions see https://www.qt.io/terms-conditions. For further 14 | ** information use the contact form at https://www.qt.io/contact-us. 15 | ** 16 | ** GNU General Public License Usage 17 | ** Alternatively, this file may be used under the terms of the GNU 18 | ** General Public License version 3 as published by the Free Software 19 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 20 | ** included in the packaging of this file. Please review the following 21 | ** information to ensure the GNU General Public License requirements will 22 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. 23 | ** 24 | ****************************************************************************/ 25 | 26 | #ifndef JOBCLIENT_H 27 | #define JOBCLIENT_H 28 | 29 | #include "processenvironment.h" 30 | #include 31 | 32 | QT_BEGIN_NAMESPACE 33 | class QSystemSemaphore; 34 | class QThread; 35 | QT_END_NAMESPACE 36 | 37 | namespace NMakeFile { 38 | 39 | class JobClientAcquireHelper; 40 | 41 | class JobClient : public QObject 42 | { 43 | Q_OBJECT 44 | public: 45 | explicit JobClient(ProcessEnvironment *environment, QObject *parent = 0); 46 | ~JobClient(); 47 | 48 | bool start(); 49 | void asyncAcquire(); 50 | bool isAcquiring() const; 51 | void release(); 52 | QString errorString() const; 53 | 54 | signals: 55 | void startAcquisition(); 56 | void acquired(); 57 | 58 | private slots: 59 | void onHelperAcquired(); 60 | 61 | private: 62 | void setError(const QString &errorMessage); 63 | 64 | ProcessEnvironment *m_environment; 65 | QString m_errorString; 66 | QSystemSemaphore *m_semaphore; 67 | QThread *m_acquireThread; 68 | JobClientAcquireHelper *m_acquireHelper; 69 | bool m_isAcquiring; 70 | }; 71 | 72 | } // namespace NMakeFile 73 | 74 | #endif // JOBCLIENT_H 75 | -------------------------------------------------------------------------------- /src/jomlib/jobclientacquirehelper.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of jom. 7 | ** 8 | ** Commercial License Usage 9 | ** Licensees holding valid commercial Qt licenses may use this file in 10 | ** accordance with the commercial license agreement provided with the 11 | ** Software or, alternatively, in accordance with the terms contained in 12 | ** a written agreement between you and The Qt Company. For licensing terms 13 | ** and conditions see https://www.qt.io/terms-conditions. For further 14 | ** information use the contact form at https://www.qt.io/contact-us. 15 | ** 16 | ** GNU General Public License Usage 17 | ** Alternatively, this file may be used under the terms of the GNU 18 | ** General Public License version 3 as published by the Free Software 19 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 20 | ** included in the packaging of this file. Please review the following 21 | ** information to ensure the GNU General Public License requirements will 22 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. 23 | ** 24 | ****************************************************************************/ 25 | 26 | #include "jobclientacquirehelper.h" 27 | 28 | namespace NMakeFile { 29 | 30 | JobClientAcquireHelper::JobClientAcquireHelper(QSystemSemaphore *semaphore) 31 | : m_semaphore(semaphore) 32 | { 33 | } 34 | 35 | void JobClientAcquireHelper::acquire() 36 | { 37 | if (!m_semaphore->acquire()) { 38 | qWarning("QSystemSemaphore::acquire failed: %s (%d)", 39 | qPrintable(m_semaphore->errorString()), m_semaphore->error()); 40 | return; 41 | } 42 | emit acquired(); 43 | } 44 | 45 | } // namespace NMakeFile 46 | -------------------------------------------------------------------------------- /src/jomlib/jobclientacquirehelper.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of jom. 7 | ** 8 | ** Commercial License Usage 9 | ** Licensees holding valid commercial Qt licenses may use this file in 10 | ** accordance with the commercial license agreement provided with the 11 | ** Software or, alternatively, in accordance with the terms contained in 12 | ** a written agreement between you and The Qt Company. For licensing terms 13 | ** and conditions see https://www.qt.io/terms-conditions. For further 14 | ** information use the contact form at https://www.qt.io/contact-us. 15 | ** 16 | ** GNU General Public License Usage 17 | ** Alternatively, this file may be used under the terms of the GNU 18 | ** General Public License version 3 as published by the Free Software 19 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 20 | ** included in the packaging of this file. Please review the following 21 | ** information to ensure the GNU General Public License requirements will 22 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. 23 | ** 24 | ****************************************************************************/ 25 | 26 | #ifndef JOBCLIENTACQUIRETHREAD_H 27 | #define JOBCLIENTACQUIRETHREAD_H 28 | 29 | #include 30 | #include 31 | 32 | namespace NMakeFile { 33 | 34 | class JobClientAcquireHelper : public QObject 35 | { 36 | Q_OBJECT 37 | public: 38 | explicit JobClientAcquireHelper(QSystemSemaphore *semaphore); 39 | 40 | public slots: 41 | void acquire(); 42 | 43 | signals: 44 | void acquired(); 45 | 46 | private: 47 | QSystemSemaphore *m_semaphore; 48 | }; 49 | 50 | } // namespace NMakeFile 51 | 52 | #endif // JOBCLIENTACQUIRETHREAD_H 53 | -------------------------------------------------------------------------------- /src/jomlib/jobserver.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of jom. 7 | ** 8 | ** Commercial License Usage 9 | ** Licensees holding valid commercial Qt licenses may use this file in 10 | ** accordance with the commercial license agreement provided with the 11 | ** Software or, alternatively, in accordance with the terms contained in 12 | ** a written agreement between you and The Qt Company. For licensing terms 13 | ** and conditions see https://www.qt.io/terms-conditions. For further 14 | ** information use the contact form at https://www.qt.io/contact-us. 15 | ** 16 | ** GNU General Public License Usage 17 | ** Alternatively, this file may be used under the terms of the GNU 18 | ** General Public License version 3 as published by the Free Software 19 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 20 | ** included in the packaging of this file. Please review the following 21 | ** information to ensure the GNU General Public License requirements will 22 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. 23 | ** 24 | ****************************************************************************/ 25 | 26 | #include "jobserver.h" 27 | #include "filetime.h" 28 | #include "helperfunctions.h" 29 | #include 30 | #include 31 | #include 32 | 33 | namespace NMakeFile { 34 | 35 | JobServer::JobServer(ProcessEnvironment *environment) 36 | : m_semaphore(0) 37 | , m_environment(environment) 38 | { 39 | } 40 | 41 | JobServer::~JobServer() 42 | { 43 | delete m_semaphore; 44 | } 45 | 46 | bool JobServer::start(int maxNumberOfJobs) 47 | { 48 | Q_ASSERT(m_environment); 49 | 50 | const quint64 randomId = (FileTime::currentTime().internalRepresentation() % UINT_MAX) 51 | ^ reinterpret_cast(&maxNumberOfJobs); 52 | const QString semaphoreKey = QLatin1String("jomsrv-") 53 | + QString::number(QCoreApplication::applicationPid()) + QLatin1Char('-') 54 | + QString::number(randomId); 55 | m_semaphore = new QSystemSemaphore(semaphoreKey, maxNumberOfJobs - 1, QSystemSemaphore::Create); 56 | if (m_semaphore->error() != QSystemSemaphore::NoError) { 57 | setError(m_semaphore->errorString()); 58 | return false; 59 | } 60 | m_environment->insert(QLatin1String("_JOMSRVKEY_"), semaphoreKey); 61 | m_environment->insert(QLatin1String("_JOMJOBCOUNT_"), QString::number(maxNumberOfJobs)); 62 | return true; 63 | } 64 | 65 | QString JobServer::errorString() const 66 | { 67 | return m_errorString; 68 | } 69 | 70 | void JobServer::setError(const QString &errorMessage) 71 | { 72 | m_errorString = errorMessage; 73 | } 74 | 75 | } // namespace NMakeFile 76 | -------------------------------------------------------------------------------- /src/jomlib/jobserver.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of jom. 7 | ** 8 | ** Commercial License Usage 9 | ** Licensees holding valid commercial Qt licenses may use this file in 10 | ** accordance with the commercial license agreement provided with the 11 | ** Software or, alternatively, in accordance with the terms contained in 12 | ** a written agreement between you and The Qt Company. For licensing terms 13 | ** and conditions see https://www.qt.io/terms-conditions. For further 14 | ** information use the contact form at https://www.qt.io/contact-us. 15 | ** 16 | ** GNU General Public License Usage 17 | ** Alternatively, this file may be used under the terms of the GNU 18 | ** General Public License version 3 as published by the Free Software 19 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 20 | ** included in the packaging of this file. Please review the following 21 | ** information to ensure the GNU General Public License requirements will 22 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. 23 | ** 24 | ****************************************************************************/ 25 | 26 | #ifndef JOBSERVER_H 27 | #define JOBSERVER_H 28 | 29 | #include "processenvironment.h" 30 | 31 | QT_BEGIN_NAMESPACE 32 | class QSystemSemaphore; 33 | QT_END_NAMESPACE 34 | 35 | namespace NMakeFile { 36 | 37 | class JobServer 38 | { 39 | public: 40 | JobServer(ProcessEnvironment *environment); 41 | ~JobServer(); 42 | 43 | bool start(int maxNumberOfJobs); 44 | QString errorString() const; 45 | 46 | private: 47 | void setError(const QString &errorMessage); 48 | 49 | QString m_errorString; 50 | QSystemSemaphore *m_semaphore; 51 | ProcessEnvironment *m_environment; 52 | }; 53 | 54 | } // namespace NMakeFile 55 | 56 | #endif // JOBSERVER_H 57 | -------------------------------------------------------------------------------- /src/jomlib/jomlib.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = lib 2 | TARGET = jomlib 3 | DESTDIR = ../../lib 4 | QT = core 5 | CONFIG += qt staticlib debug_and_release build_all 6 | DEFINES += _CRT_SECURE_NO_WARNINGS 7 | DEFINES += QT_NO_CAST_FROM_ASCII QT_NO_CAST_TO_ASCII 8 | PRECOMPILED_HEADER = stable.h 9 | 10 | build_pass:CONFIG(debug, debug|release) { 11 | TARGET = $$join(TARGET,,,d) 12 | } 13 | 14 | isEmpty(MSYSPATH):MSYSPATH=C:/msys 15 | !exists($$MSYSPATH): MSYSPATH=D:/msys 16 | !exists($$MSYSPATH): MSYSPATH=C:/msys64 17 | MSYS_BIN_PATHS = 1.0/bin usr/bin 18 | !exists($$MSYSPATH) { 19 | !build_pass:message("Can't locate path to MSYS. This is needed for flex.") 20 | } else { 21 | for(bin_path, MSYS_BIN_PATHS) { 22 | test_flex_bin = $$MSYSPATH/$$bin_path/flex.exe 23 | exists($$test_flex_bin):FLEX_BIN = $$test_flex_bin 24 | } 25 | isEmpty(FLEX_BIN) { 26 | !build_pass:message("MSYSPATH is set but flex cannot be found.") 27 | } 28 | } 29 | !isEmpty(FLEX_BIN) { 30 | # One special extra compiler for ppexpr.l because 31 | # msys flex does not understand backslashes and I have no way 32 | # to translate the slashes in ${QMAKE_FILE_IN}. /me rolls eyes... 33 | PPEXPR_FLEX_FILE = $$PWD/ppexpr.l 34 | flex.name = flex ppexpr.l 35 | flex.input = PPEXPR_FLEX_FILE 36 | flex.output = ${QMAKE_FILE_BASE}-lex.inc 37 | flex.commands = $$FLEX_BIN --noline $$PPEXPR_FLEX_FILE 38 | flex.CONFIG += no_link explicit_dependencies 39 | QMAKE_EXTRA_COMPILERS += flex 40 | 41 | QLALR_FILES = ppexpr.g 42 | qlalr.name = qlalr 43 | qlalr.input = QLALR_FILES 44 | qlalr.output = ${QMAKE_FILE_BASE}_grammar.cpp 45 | qlalr.commands = $$[QT_INSTALL_BINS]\\qlalr.exe --no-lines ${QMAKE_FILE_IN} 46 | qlalr.depends = $$PWD/${QMAKE_FILE_BASE}.l 47 | qlalr.dependency_type = TYPE_C 48 | qlalr.CONFIG += no_link explicit_dependencies 49 | QMAKE_EXTRA_COMPILERS += qlalr 50 | } 51 | 52 | win32-* { 53 | HEADERS += \ 54 | iocompletionport.h 55 | SOURCES += \ 56 | jomprocess.cpp \ 57 | iocompletionport.cpp 58 | } else { 59 | DEFINES += USE_QPROCESS 60 | SOURCES += \ 61 | jomprocess_qt.cpp 62 | } 63 | 64 | HEADERS += \ 65 | fastfileinfo.h \ 66 | filetime.h \ 67 | helperfunctions.h \ 68 | jobserver.h \ 69 | makefile.h \ 70 | makefilefactory.h \ 71 | makefilelinereader.h \ 72 | macrotable.h \ 73 | exception.h \ 74 | dependencygraph.h \ 75 | options.h \ 76 | parser.h \ 77 | preprocessor.h \ 78 | ppexprparser.h \ 79 | targetexecutor.h \ 80 | commandexecutor.h \ 81 | jomprocess.h \ 82 | processenvironment.h \ 83 | jobclient.h \ 84 | jobclientacquirehelper.h 85 | 86 | SOURCES += \ 87 | fastfileinfo.cpp \ 88 | filetime.cpp \ 89 | helperfunctions.cpp \ 90 | jobserver.cpp \ 91 | macrotable.cpp \ 92 | makefile.cpp \ 93 | makefilefactory.cpp \ 94 | makefilelinereader.cpp \ 95 | exception.cpp \ 96 | dependencygraph.cpp \ 97 | options.cpp \ 98 | parser.cpp \ 99 | preprocessor.cpp \ 100 | ppexpr_grammar.cpp \ 101 | ppexprparser.cpp \ 102 | targetexecutor.cpp \ 103 | commandexecutor.cpp \ 104 | jobclient.cpp \ 105 | jobclientacquirehelper.cpp 106 | 107 | OTHER_FILES += \ 108 | ppexpr.g \ 109 | ppexpr.l 110 | -------------------------------------------------------------------------------- /src/jomlib/jomprocess.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of jom. 7 | ** 8 | ** Commercial License Usage 9 | ** Licensees holding valid commercial Qt licenses may use this file in 10 | ** accordance with the commercial license agreement provided with the 11 | ** Software or, alternatively, in accordance with the terms contained in 12 | ** a written agreement between you and The Qt Company. For licensing terms 13 | ** and conditions see https://www.qt.io/terms-conditions. For further 14 | ** information use the contact form at https://www.qt.io/contact-us. 15 | ** 16 | ** GNU General Public License Usage 17 | ** Alternatively, this file may be used under the terms of the GNU 18 | ** General Public License version 3 as published by the Free Software 19 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 20 | ** included in the packaging of this file. Please review the following 21 | ** information to ensure the GNU General Public License requirements will 22 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. 23 | ** 24 | ****************************************************************************/ 25 | 26 | #ifndef PROCESS_H 27 | #define PROCESS_H 28 | 29 | #include "processenvironment.h" 30 | #include 31 | #include 32 | 33 | #ifdef USE_QPROCESS 34 | 35 | #include 36 | 37 | namespace NMakeFile { 38 | 39 | class Process : public QProcess 40 | { 41 | Q_OBJECT 42 | public: 43 | enum ProcessError 44 | { 45 | UnknownError, 46 | FailedToStart, 47 | Crashed 48 | }; 49 | 50 | enum ExitStatus 51 | { 52 | NormalExit, 53 | CrashExit 54 | }; 55 | 56 | Process(QObject *parent = 0); 57 | void setBufferedOutput(bool bufferedOutput); 58 | bool isBufferedOutputSet() const; 59 | void setEnvironment(const ProcessEnvironment &e); 60 | ProcessEnvironment environment() const; 61 | bool isRunning() const; 62 | void start(const QString &commandLine); 63 | void writeToStdOutBuffer(const QByteArray &output); 64 | void writeToStdErrBuffer(const QByteArray &output); 65 | ExitStatus exitStatus() const; 66 | 67 | signals: 68 | void error(Process::ProcessError); 69 | void finished(int, Process::ExitStatus); 70 | 71 | private slots: 72 | void forwardError(QProcess::ProcessError); 73 | void forwardFinished(int, QProcess::ExitStatus); 74 | }; 75 | 76 | } // namespace NMakeFile 77 | 78 | #else 79 | 80 | namespace NMakeFile { 81 | 82 | class Process : public QObject 83 | { 84 | Q_OBJECT 85 | public: 86 | explicit Process(QObject *parent = 0); 87 | ~Process(); 88 | 89 | enum ProcessError 90 | { 91 | UnknownError, 92 | FailedToStart, 93 | Crashed 94 | }; 95 | 96 | enum ExitStatus 97 | { 98 | NormalExit, 99 | CrashExit 100 | }; 101 | 102 | enum ProcessState 103 | { 104 | NotRunning, 105 | Starting, 106 | Running 107 | }; 108 | 109 | void setBufferedOutput(bool b); 110 | bool isBufferedOutputSet() const { return m_bufferedOutput; } 111 | void writeToStdOutBuffer(const QByteArray &output); 112 | void writeToStdErrBuffer(const QByteArray &output); 113 | void setWorkingDirectory(const QString &path); 114 | const QString &workingDirectory() const { return m_workingDirectory; } 115 | void setEnvironment(const ProcessEnvironment &environment); 116 | const ProcessEnvironment &environment() const { return m_environment; } 117 | int exitCode() const { return m_exitCode; } 118 | ExitStatus exitStatus() const { return m_exitStatus; } 119 | bool isRunning() const { return m_state == Running; } 120 | 121 | signals: 122 | void error(Process::ProcessError); 123 | void finished(int, Process::ExitStatus); 124 | 125 | public slots: 126 | void start(const QString &commandLine); 127 | bool waitForFinished(); 128 | 129 | private: 130 | void printBufferedOutput(); 131 | 132 | private slots: 133 | void tryToRetrieveExitCode(); 134 | void onProcessFinished(); 135 | 136 | private: 137 | class ProcessPrivate *d; 138 | QString m_workingDirectory; 139 | ProcessEnvironment m_environment; 140 | QByteArray m_envBlock; 141 | ProcessState m_state; 142 | int m_exitCode; 143 | ExitStatus m_exitStatus; 144 | bool m_bufferedOutput; 145 | 146 | friend class ProcessPrivate; 147 | }; 148 | 149 | } // namespace NMakeFile 150 | 151 | #endif // USE_QPROCESS 152 | 153 | #endif // PROCESS_H 154 | -------------------------------------------------------------------------------- /src/jomlib/jomprocess_qt.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of jom. 7 | ** 8 | ** Commercial License Usage 9 | ** Licensees holding valid commercial Qt licenses may use this file in 10 | ** accordance with the commercial license agreement provided with the 11 | ** Software or, alternatively, in accordance with the terms contained in 12 | ** a written agreement between you and The Qt Company. For licensing terms 13 | ** and conditions see https://www.qt.io/terms-conditions. For further 14 | ** information use the contact form at https://www.qt.io/contact-us. 15 | ** 16 | ** GNU General Public License Usage 17 | ** Alternatively, this file may be used under the terms of the GNU 18 | ** General Public License version 3 as published by the Free Software 19 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 20 | ** included in the packaging of this file. Please review the following 21 | ** information to ensure the GNU General Public License requirements will 22 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. 23 | ** 24 | ****************************************************************************/ 25 | 26 | #include "jomprocess.h" 27 | #include 28 | 29 | namespace NMakeFile { 30 | 31 | Process::Process(QObject *parent) 32 | : QProcess(parent) 33 | { 34 | connect(this, SIGNAL(error(QProcess::ProcessError)), SLOT(forwardError(QProcess::ProcessError))); 35 | connect(this, SIGNAL(finished(int, QProcess::ExitStatus)), SLOT(forwardFinished(int, QProcess::ExitStatus))); 36 | } 37 | 38 | void Process::setBufferedOutput(bool bufferedOutput) 39 | { 40 | QProcess::setProcessChannelMode(bufferedOutput ? SeparateChannels : ForwardedChannels); 41 | } 42 | 43 | bool Process::isBufferedOutputSet() const 44 | { 45 | return QProcess::processChannelMode() == SeparateChannels; 46 | } 47 | 48 | void Process::setEnvironment(const ProcessEnvironment &e) 49 | { 50 | QProcessEnvironment qpenv; 51 | for (ProcessEnvironment::const_iterator it = e.constBegin(); it != e.constEnd(); ++it) 52 | qpenv.insert(it.key().toQString(), it.value()); 53 | QProcess::setProcessEnvironment(qpenv); 54 | } 55 | 56 | ProcessEnvironment Process::environment() const 57 | { 58 | ProcessEnvironment env; 59 | const QProcessEnvironment qpenv = QProcess::processEnvironment(); 60 | foreach (const QString &key, qpenv.keys()) 61 | env.insert(key, qpenv.value(key)); 62 | return env; 63 | } 64 | 65 | bool Process::isRunning() const 66 | { 67 | return QProcess::state() == QProcess::Running; 68 | } 69 | 70 | void Process::start(const QString &commandLine) 71 | { 72 | QProcess::start(commandLine); 73 | QProcess::waitForStarted(); 74 | } 75 | 76 | void Process::writeToStdOutBuffer(const QByteArray &output) 77 | { 78 | fputs(output.data(), stdout); 79 | fflush(stdout); 80 | } 81 | 82 | void Process::writeToStdErrBuffer(const QByteArray &output) 83 | { 84 | fputs(output.data(), stderr); 85 | fflush(stderr); 86 | } 87 | 88 | Process::ExitStatus Process::exitStatus() const 89 | { 90 | return static_cast(QProcess::exitStatus()); 91 | } 92 | 93 | void Process::forwardError(QProcess::ProcessError qe) 94 | { 95 | ProcessError e; 96 | switch (qe) { 97 | case QProcess::FailedToStart: 98 | e = FailedToStart; 99 | break; 100 | case QProcess::Crashed: 101 | e = Crashed; 102 | break; 103 | default: 104 | e = UnknownError; 105 | } 106 | emit error(e); 107 | } 108 | 109 | void Process::forwardFinished(int exitCode, QProcess::ExitStatus status) 110 | { 111 | emit finished(exitCode, static_cast(status)); 112 | } 113 | 114 | } // namespace NMakeFile 115 | -------------------------------------------------------------------------------- /src/jomlib/macrotable.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of jom. 7 | ** 8 | ** Commercial License Usage 9 | ** Licensees holding valid commercial Qt licenses may use this file in 10 | ** accordance with the commercial license agreement provided with the 11 | ** Software or, alternatively, in accordance with the terms contained in 12 | ** a written agreement between you and The Qt Company. For licensing terms 13 | ** and conditions see https://www.qt.io/terms-conditions. For further 14 | ** information use the contact form at https://www.qt.io/contact-us. 15 | ** 16 | ** GNU General Public License Usage 17 | ** Alternatively, this file may be used under the terms of the GNU 18 | ** General Public License version 3 as published by the Free Software 19 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 20 | ** included in the packaging of this file. Please review the following 21 | ** information to ensure the GNU General Public License requirements will 22 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. 23 | ** 24 | ****************************************************************************/ 25 | 26 | #ifndef MACROTABLE_H 27 | #define MACROTABLE_H 28 | 29 | #include "processenvironment.h" 30 | 31 | #include 32 | #include 33 | 34 | namespace NMakeFile { 35 | 36 | class MacroTable 37 | { 38 | public: 39 | static const QChar fileNameMacroMagicEscape; 40 | 41 | MacroTable(); 42 | ~MacroTable(); 43 | 44 | void setEnvironment(const ProcessEnvironment &e) { m_environment = e; } 45 | const ProcessEnvironment &environment() const { return m_environment; } 46 | 47 | bool isMacroDefined(const QString& name) const; 48 | bool isMacroNameValid(const QString& name) const; 49 | QString macroValue(const QString& macroName) const; 50 | void defineEnvironmentMacroValue(const QString& name, const QString& value, bool readOnly = false); 51 | void defineCommandLineMacroValue(const QString &name, const QString &value); 52 | void defineImplicitCommandLineMacroValue(const QString &name, const QString &value); 53 | void setMacroValue(const QString& name, const QString& value); 54 | void setMacroValue(const char *szStr, const QString& value) { setMacroValue(QString::fromLatin1(szStr), value); } 55 | void setMacroValue(const char *szStr, const char *szValue) { setMacroValue(QString::fromLatin1(szStr), QString::fromLatin1(szValue)); } 56 | void predefineValue(const QString &name, const QString &value); 57 | void predefineValue(const char *szStr, const QString &value) { predefineValue(QString::fromLatin1(szStr), value); } 58 | void predefineValue(const char *szStr, const char *szValue) { predefineValue(QString::fromLatin1(szStr), QString::fromLatin1(szValue)); } 59 | void undefineMacro(const QString& name); 60 | QString expandMacros(const QString& str, bool inDependentsLine = false) const; 61 | void dump() const; 62 | 63 | struct Substitution 64 | { 65 | QString before; 66 | QString after; 67 | }; 68 | 69 | static Substitution parseSubstitutionStatement(const QString &str, int substitutionStartIdx, int ¯oInvokationEndIdx); 70 | static void applySubstitution(const Substitution &substitution, QString &value); 71 | 72 | private: 73 | enum class MacroSource 74 | { 75 | CommandLine, 76 | CommandLineImplicit, 77 | MakeFile, 78 | Environment, 79 | Predefinition 80 | }; 81 | 82 | struct MacroData 83 | { 84 | MacroSource source = MacroSource::MakeFile; 85 | bool isReadOnly = false; 86 | QString value; 87 | }; 88 | 89 | void setMacroValueImpl(const QString &name, const QString &value, MacroSource source); 90 | void defineCommandLineMacroValueImpl(const QString &name, const QString &value, 91 | MacroSource source); 92 | MacroData* internalSetMacroValue(const QString &name, const QString &value, 93 | bool ignoreReadOnly = false); 94 | void setEnvironmentVariable(const QString& name, const QString& value); 95 | QString expandMacros(const QString& str, bool inDependentsLine, QSet& usedMacros) const; 96 | QString cycleCheckedMacroValue(const QString& macroName, QSet& usedMacros) const; 97 | 98 | QHash m_macros; 99 | ProcessEnvironment m_environment; 100 | }; 101 | 102 | } // namespace NMakeFile 103 | 104 | #endif // MACROTABLE_H 105 | -------------------------------------------------------------------------------- /src/jomlib/makefile.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of jom. 7 | ** 8 | ** Commercial License Usage 9 | ** Licensees holding valid commercial Qt licenses may use this file in 10 | ** accordance with the commercial license agreement provided with the 11 | ** Software or, alternatively, in accordance with the terms contained in 12 | ** a written agreement between you and The Qt Company. For licensing terms 13 | ** and conditions see https://www.qt.io/terms-conditions. For further 14 | ** information use the contact form at https://www.qt.io/contact-us. 15 | ** 16 | ** GNU General Public License Usage 17 | ** Alternatively, this file may be used under the terms of the GNU 18 | ** General Public License version 3 as published by the Free Software 19 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 20 | ** included in the packaging of this file. Please review the following 21 | ** information to ensure the GNU General Public License requirements will 22 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. 23 | ** 24 | ****************************************************************************/ 25 | 26 | #ifndef MAKEFILE_H 27 | #define MAKEFILE_H 28 | 29 | #include "fastfileinfo.h" 30 | #include "macrotable.h" 31 | #include 32 | #include 33 | #include 34 | 35 | namespace NMakeFile { 36 | 37 | class Options; 38 | 39 | class InlineFile { 40 | public: 41 | InlineFile(); 42 | InlineFile(const InlineFile& rhs); 43 | 44 | bool m_keep; 45 | bool m_unicode; 46 | QString m_filename; // optional 47 | QString m_content; 48 | }; 49 | 50 | class Command { 51 | public: 52 | Command(); 53 | Command(const Command& rhs); 54 | ~Command(); 55 | 56 | void evaluateModifiers(); 57 | 58 | QString m_commandLine; 59 | QList m_inlineFiles; 60 | unsigned int m_maxExitCode; // greatest allowed exit code 61 | bool m_silent; 62 | bool m_singleExecution; // Execute this command for each dependent, if the command contains $** or $?. 63 | }; 64 | 65 | class CommandContainer { 66 | public: 67 | CommandContainer() {}; 68 | CommandContainer(const CommandContainer& rhs) 69 | : m_commands(rhs.m_commands) 70 | {} 71 | 72 | QList m_commands; 73 | }; 74 | 75 | class CommandExecutor; 76 | class InferenceRule; 77 | class Makefile; 78 | 79 | class DescriptionBlock : public CommandContainer { 80 | public: 81 | DescriptionBlock(Makefile* mkfile); 82 | 83 | void expandFileNameMacrosForDependents(); 84 | void expandFileNameMacros(); 85 | 86 | void setTargetName(const QString& name); 87 | 88 | /** 89 | * Returns the name of the target. 90 | * This string may be surrounded by double quotes! 91 | */ 92 | QString targetName() const 93 | { 94 | return m_targetName; 95 | } 96 | 97 | /** 98 | * Returns the makefile, this target belongs to. 99 | */ 100 | Makefile* makefile() const { return m_pMakefile; } 101 | 102 | QStringList m_dependents; 103 | FileTime m_timeStamp; 104 | bool m_bFileExists; 105 | bool m_bVisitedByCycleCheck; 106 | bool m_bNoCyclesRootedHere; 107 | QVector m_inferenceRules; 108 | 109 | enum AddCommandsState { ACSUnknown, ACSEnabled, ACSDisabled }; 110 | AddCommandsState m_canAddCommands; 111 | 112 | private: 113 | void expandFileNameMacros(Command& command, int depIdx); 114 | void expandFileNameMacros(QString& str, int depIdx, bool dependentsForbidden); 115 | QStringList getFileNameMacroValues(const QStringRef& str, int& replacementLength, int depIdx, 116 | bool dependentsForbidden); 117 | 118 | private: 119 | QString m_targetName; 120 | Makefile* m_pMakefile; 121 | }; 122 | 123 | class InferenceRule : public CommandContainer { 124 | public: 125 | InferenceRule(); 126 | InferenceRule(const InferenceRule& rhs); 127 | 128 | bool operator == (const InferenceRule& rhs) const; 129 | 130 | QString inferredDependent(const QString &targetName) const; 131 | 132 | bool m_batchMode; 133 | QString m_fromSearchPath; 134 | QString m_fromExtension; 135 | QString m_toSearchPath; 136 | QString m_toExtension; 137 | int m_priority; // priority < 0 means: not applicable 138 | }; 139 | 140 | class Makefile 141 | { 142 | public: 143 | Makefile(const QString &fileName); 144 | ~Makefile(); 145 | 146 | void clear(); 147 | 148 | void append(DescriptionBlock* target) 149 | { 150 | m_targets[target->targetName().toLower()] = target; 151 | if (!m_firstTarget) m_firstTarget = target; 152 | } 153 | 154 | DescriptionBlock* firstTarget() 155 | { 156 | return m_firstTarget; 157 | } 158 | 159 | DescriptionBlock* target(const QString& name) const 160 | { 161 | DescriptionBlock* result = 0; 162 | const QString lowerName = name.toLower(); 163 | result = m_targets.value(lowerName, 0); 164 | if (result) 165 | return result; 166 | 167 | QString systemName = lowerName; 168 | result = m_targets.value(systemName.replace(QLatin1Char('/'), QLatin1Char('\\')), 0); 169 | if (result) 170 | return result; 171 | 172 | return result; 173 | } 174 | 175 | const QHash& targets() const 176 | { 177 | return m_targets; 178 | } 179 | 180 | const QStringList& preciousTargets() const 181 | { 182 | return m_preciousTargets; 183 | } 184 | 185 | const QVector& inferenceRules() const 186 | { 187 | return m_inferenceRules; 188 | } 189 | 190 | const QString &fileName() const { return m_fileName; } 191 | const QString &dirPath() const; 192 | void setOptions(Options *o) { m_options = o; } 193 | const Options* options() const { return m_options; } 194 | void setParallelExecutionDisabled(bool disabled) { m_parallelExecutionDisabled = disabled; } 195 | bool isParallelExecutionDisabled() const { return m_parallelExecutionDisabled; } 196 | 197 | void setMacroTable(MacroTable *mt) { m_macroTable = mt; } 198 | const MacroTable* macroTable() const { return m_macroTable; } 199 | 200 | void dumpTarget(DescriptionBlock*, uchar level = 0) const; 201 | void dumpTargets() const; 202 | void dumpInferenceRules() const; 203 | void invalidateTimeStamps(); 204 | void applyInferenceRules(QList targets); 205 | void addInferenceRule(InferenceRule *rule); 206 | void calculateInferenceRulePriorities(const QStringList &suffixes); 207 | void addPreciousTarget(const QString& targetName); 208 | 209 | private: 210 | void filterRulesByDependent(QVector& rules, const QString& targetName); 211 | QStringList findInferredDependents(InferenceRule* rule, const QStringList& dependents); 212 | void applyInferenceRules(DescriptionBlock* target); 213 | void applyInferenceRule(DescriptionBlock* target, const InferenceRule *rule, bool applyingBatchMode = false); 214 | void applyInferenceRule(QList &batch, const InferenceRule *rule); 215 | 216 | private: 217 | QString m_fileName; 218 | mutable QString m_dirPath; 219 | DescriptionBlock* m_firstTarget; 220 | QHash m_targets; 221 | QStringList m_preciousTargets; 222 | QVector m_inferenceRules; 223 | MacroTable* m_macroTable; 224 | Options* m_options; 225 | QSet m_batchModeRules; 226 | QMultiHash m_batchModeTargets; 227 | bool m_parallelExecutionDisabled; 228 | }; 229 | 230 | } // namespace NMakeFile 231 | 232 | #endif // MAKEFILE_H 233 | -------------------------------------------------------------------------------- /src/jomlib/makefilefactory.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of jom. 7 | ** 8 | ** Commercial License Usage 9 | ** Licensees holding valid commercial Qt licenses may use this file in 10 | ** accordance with the commercial license agreement provided with the 11 | ** Software or, alternatively, in accordance with the terms contained in 12 | ** a written agreement between you and The Qt Company. For licensing terms 13 | ** and conditions see https://www.qt.io/terms-conditions. For further 14 | ** information use the contact form at https://www.qt.io/contact-us. 15 | ** 16 | ** GNU General Public License Usage 17 | ** Alternatively, this file may be used under the terms of the GNU 18 | ** General Public License version 3 as published by the Free Software 19 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 20 | ** included in the packaging of this file. Please review the following 21 | ** information to ensure the GNU General Public License requirements will 22 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. 23 | ** 24 | ****************************************************************************/ 25 | 26 | #include "exception.h" 27 | #include "makefilefactory.h" 28 | #include "macrotable.h" 29 | #include "makefile.h" 30 | #include "options.h" 31 | #include "parser.h" 32 | #include "preprocessor.h" 33 | 34 | #include 35 | #include 36 | 37 | namespace NMakeFile { 38 | 39 | MakefileFactory::MakefileFactory() 40 | : m_makefile(0), 41 | m_errorType(NoError) 42 | { 43 | } 44 | 45 | void MakefileFactory::setEnvironment(const QStringList &env) 46 | { 47 | for (QStringList::const_iterator it = env.begin(); it != env.end(); ++it) { 48 | int idx = it->indexOf(QLatin1Char('=')); 49 | if (idx >= 0) 50 | m_environment.insert(it->left(idx), it->mid(idx + 1)); 51 | } 52 | } 53 | 54 | void MakefileFactory::clear() 55 | { 56 | m_makefile = 0; 57 | m_errorType = NoError; 58 | m_errorString.clear(); 59 | m_activeTargets.clear(); 60 | } 61 | 62 | static void readEnvironment(const ProcessEnvironment &environment, MacroTable *macroTable, bool forceReadOnly) 63 | { 64 | ProcessEnvironment::const_iterator it = environment.begin(); 65 | for (; it != environment.end(); ++it) 66 | macroTable->defineEnvironmentMacroValue(it.key().toQString(), it.value(), forceReadOnly); 67 | } 68 | 69 | /** 70 | * Returns true, if the path contains some whitespace. 71 | */ 72 | static bool isComplexPathName(const QString& path) 73 | { 74 | for (int i=path.length()-1; i > 0; i--) { 75 | const QChar ch = path.at(i); 76 | if (ch.isSpace()) 77 | return true; 78 | } 79 | return false; 80 | } 81 | 82 | /** 83 | * Enclose the path in double quotes, if its a complex one. 84 | */ 85 | static QString encloseInDoubleQuotesIfNeeded(const QString& path) 86 | { 87 | if (isComplexPathName(path)) { 88 | QString result(QLatin1Char('"')); 89 | result.append(path); 90 | result.append(QLatin1Char('"')); 91 | return result; 92 | } 93 | return path; 94 | } 95 | 96 | bool MakefileFactory::apply(const QStringList& commandLineArguments, Options **outopt) 97 | { 98 | if (m_makefile) 99 | clear(); 100 | 101 | Options *options = new Options; 102 | if (outopt) 103 | *outopt = options; 104 | MacroTable *macroTable = new MacroTable; 105 | macroTable->setEnvironment(m_environment); 106 | 107 | QString filename; 108 | if (!options->readCommandLineArguments(commandLineArguments, filename, m_activeTargets, *macroTable)) { 109 | m_errorType = CommandLineError; 110 | return false; 111 | } 112 | if (options->showUsageAndExit || options->showVersionAndExit) 113 | return true; 114 | 115 | if (!options->stderrFile.isEmpty()) { 116 | // Try to open the file for writing. 117 | const wchar_t *wszFileName = reinterpret_cast(options->stderrFile.utf16()); 118 | FILE *f = _wfopen(wszFileName, L"w"); 119 | if (!f) { 120 | m_errorString = QLatin1String("Cannot open stderr file for writing."); 121 | m_errorType = IOError; 122 | return false; 123 | } 124 | fclose(f); 125 | if (!_wfreopen(wszFileName, L"w", stderr)) { 126 | m_errorString = QLatin1String("Cannot reopen stderr handle for writing."); 127 | m_errorType = IOError; 128 | return false; 129 | } 130 | } 131 | 132 | options->fullAppPath = QDir::toNativeSeparators(QCoreApplication::applicationFilePath()); 133 | 134 | readEnvironment(m_environment, macroTable, options->overrideEnvVarMacros); 135 | if (!options->ignorePredefinedRulesAndMacros) { 136 | macroTable->predefineValue("MAKE", encloseInDoubleQuotesIfNeeded(options->fullAppPath)); 137 | macroTable->predefineValue("MAKEDIR", encloseInDoubleQuotesIfNeeded( 138 | QDir::toNativeSeparators(QDir::currentPath()))); 139 | macroTable->predefineValue("AS", "ml"); // Macro Assembler 140 | macroTable->predefineValue("ASFLAGS", QString()); 141 | macroTable->predefineValue("BC", "bc"); // Basic Compiler 142 | macroTable->predefineValue("BCFLAGS", QString()); 143 | macroTable->predefineValue("CC", "cl"); // C Compiler 144 | macroTable->predefineValue("CCFLAGS", QString()); 145 | macroTable->predefineValue("COBOL", "cobol"); // COBOL Compiler 146 | macroTable->predefineValue("COBOLFLAGS", QString()); 147 | macroTable->predefineValue("CPP", "cl"); // C++ Compiler 148 | macroTable->predefineValue("CPPFLAGS", QString()); 149 | macroTable->predefineValue("CXX", "cl"); // C++ Compiler 150 | macroTable->predefineValue("CXXFLAGS", QString()); 151 | macroTable->predefineValue("FOR", "fl"); // FORTRAN Compiler 152 | macroTable->predefineValue("FORFLAGS", QString()); 153 | macroTable->predefineValue("PASCAL", "pl"); // Pascal Compiler 154 | macroTable->predefineValue("PASCALFLAGS", QString()); 155 | macroTable->predefineValue("RC", "rc"); // Resource Compiler 156 | macroTable->predefineValue("RCFLAGS", QString()); 157 | } 158 | 159 | try { 160 | m_makefile = new Makefile(filename); 161 | m_makefile->setOptions(options); 162 | m_makefile->setMacroTable(macroTable); 163 | Preprocessor preprocessor; 164 | preprocessor.setMacroTable(macroTable); 165 | preprocessor.openFile(filename); 166 | Parser parser; 167 | parser.apply(&preprocessor, m_makefile, m_activeTargets); 168 | } catch (Exception &e) { 169 | m_errorType = ParserError; 170 | m_errorString = e.toString(); 171 | } 172 | 173 | return m_errorType == NoError; 174 | } 175 | 176 | } //namespace NMakeFile 177 | -------------------------------------------------------------------------------- /src/jomlib/makefilefactory.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of jom. 7 | ** 8 | ** Commercial License Usage 9 | ** Licensees holding valid commercial Qt licenses may use this file in 10 | ** accordance with the commercial license agreement provided with the 11 | ** Software or, alternatively, in accordance with the terms contained in 12 | ** a written agreement between you and The Qt Company. For licensing terms 13 | ** and conditions see https://www.qt.io/terms-conditions. For further 14 | ** information use the contact form at https://www.qt.io/contact-us. 15 | ** 16 | ** GNU General Public License Usage 17 | ** Alternatively, this file may be used under the terms of the GNU 18 | ** General Public License version 3 as published by the Free Software 19 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 20 | ** included in the packaging of this file. Please review the following 21 | ** information to ensure the GNU General Public License requirements will 22 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. 23 | ** 24 | ****************************************************************************/ 25 | 26 | #ifndef MAKEFILEFACTORY_H 27 | #define MAKEFILEFACTORY_H 28 | 29 | #include "processenvironment.h" 30 | #include 31 | 32 | namespace NMakeFile { 33 | 34 | class Makefile; 35 | class Options; 36 | 37 | class MakefileFactory 38 | { 39 | public: 40 | MakefileFactory(); 41 | void setEnvironment(const QStringList& env); 42 | bool apply(const QStringList& commandLineArguments, Options **outopt = 0); 43 | 44 | enum ErrorType { 45 | NoError, 46 | CommandLineError, 47 | ParserError, 48 | IOError 49 | }; 50 | 51 | Makefile* makefile() { return m_makefile; } 52 | const QStringList& activeTargets() const { return m_activeTargets; } 53 | const QString& errorString() const { return m_errorString; } 54 | ErrorType errorType() const { return m_errorType; } 55 | 56 | private: 57 | void clear(); 58 | 59 | private: 60 | Makefile* m_makefile; 61 | ProcessEnvironment m_environment; 62 | QStringList m_activeTargets; 63 | QString m_errorString; 64 | ErrorType m_errorType; 65 | }; 66 | 67 | } // namespace NMakeFile 68 | 69 | #endif // MAKEFILEFACTORY_H 70 | -------------------------------------------------------------------------------- /src/jomlib/makefilelinereader.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of jom. 7 | ** 8 | ** Commercial License Usage 9 | ** Licensees holding valid commercial Qt licenses may use this file in 10 | ** accordance with the commercial license agreement provided with the 11 | ** Software or, alternatively, in accordance with the terms contained in 12 | ** a written agreement between you and The Qt Company. For licensing terms 13 | ** and conditions see https://www.qt.io/terms-conditions. For further 14 | ** information use the contact form at https://www.qt.io/contact-us. 15 | ** 16 | ** GNU General Public License Usage 17 | ** Alternatively, this file may be used under the terms of the GNU 18 | ** General Public License version 3 as published by the Free Software 19 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 20 | ** included in the packaging of this file. Please review the following 21 | ** information to ensure the GNU General Public License requirements will 22 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. 23 | ** 24 | ****************************************************************************/ 25 | 26 | #include "makefilelinereader.h" 27 | #include "helperfunctions.h" 28 | #include 29 | #include 30 | 31 | namespace NMakeFile { 32 | 33 | MakefileLineReader::MakefileLineReader(const QString& filename) 34 | : m_file(filename), 35 | m_nLineBufferSize(m_nInitialLineBufferSize), 36 | m_nLineNumber(0) 37 | { 38 | m_lineBuffer = reinterpret_cast( malloc(m_nLineBufferSize) ); 39 | } 40 | 41 | MakefileLineReader::~MakefileLineReader() 42 | { 43 | close(); 44 | free(m_lineBuffer); 45 | } 46 | 47 | bool MakefileLineReader::open() 48 | { 49 | if (!m_file.open(QIODevice::ReadOnly | QIODevice::Text)) 50 | return false; 51 | 52 | // check BOM 53 | enum FileEncoding { FCLatin1, FCUTF8, FCUTF16 }; 54 | FileEncoding fileEncoding = FCLatin1; 55 | QByteArray buf = m_file.peek(3); 56 | if (buf.startsWith("\xFF\xFE")) 57 | fileEncoding = FCUTF16; 58 | else if (buf.startsWith("\xEF\xBB\xBF")) 59 | fileEncoding = FCUTF8; 60 | 61 | if (fileEncoding == FCLatin1) { 62 | m_readLineImpl = &NMakeFile::MakefileLineReader::readLine_impl_local8bit; 63 | } else { 64 | m_readLineImpl = &NMakeFile::MakefileLineReader::readLine_impl_unicode; 65 | m_textStream.setCodec(fileEncoding == FCUTF8 ? "UTF-8" : "UTF-16"); 66 | m_textStream.setAutoDetectUnicode(false); 67 | m_textStream.setDevice(&m_file); 68 | } 69 | 70 | return true; 71 | } 72 | 73 | void MakefileLineReader::close() 74 | { 75 | m_file.close(); 76 | } 77 | 78 | void MakefileLineReader::growLineBuffer(size_t nGrow) 79 | { 80 | //fprintf(stderr, "realloc %d -> %d\n", m_nLineBufferSize, m_nLineBufferSize + nGrow); 81 | m_nLineBufferSize += nGrow; 82 | m_lineBuffer = reinterpret_cast(realloc(m_lineBuffer, m_nLineBufferSize)); 83 | } 84 | 85 | /** 86 | * This function reads lines from a makefile and 87 | * - ignores all lines that start with # 88 | * - combines multi-lines (lines with \ at the end) into a single long line 89 | * - handles the ^ escape character for \ and \n at the end 90 | */ 91 | MakefileLine MakefileLineReader::readLine(bool bInlineFileMode) 92 | { 93 | if (bInlineFileMode) { 94 | m_nLineNumber++; 95 | return MakefileLine{ QString::fromLatin1(m_file.readLine()) }; 96 | } 97 | 98 | return (this->*m_readLineImpl)(); 99 | } 100 | 101 | /** 102 | * readLine implementation optimized for 8 bit files. 103 | */ 104 | MakefileLine MakefileLineReader::readLine_impl_local8bit() 105 | { 106 | size_t bytesRead; 107 | do { 108 | m_nLineNumber++; 109 | const qint64 n = m_file.readLine(m_lineBuffer, m_nLineBufferSize - 1); 110 | if (n <= 0) 111 | return {}; 112 | 113 | bytesRead = n; 114 | while (m_lineBuffer[bytesRead - 1] != '\n') { 115 | if (m_file.atEnd()) { 116 | // The file didn't end with a newline. 117 | // Code below relies on having a trailing newline. 118 | // We're imitating it by increasing the string length. 119 | if (bytesRead >= (m_nLineBufferSize - 2)) 120 | growLineBuffer(1); 121 | ++bytesRead; 122 | break; 123 | } 124 | 125 | growLineBuffer(m_nLineBufferGrowSize); 126 | int moreBytesRead = m_file.readLine(m_lineBuffer + bytesRead, m_nLineBufferSize - 1 - bytesRead); 127 | if (moreBytesRead <= 0) 128 | break; 129 | bytesRead += moreBytesRead; 130 | } 131 | 132 | } while (m_lineBuffer[0] == '#'); 133 | m_lineBuffer[bytesRead] = '\0'; 134 | 135 | MakefileLine line; 136 | char* buf = m_lineBuffer; 137 | int bufLength = static_cast(bytesRead); 138 | if (bufLength >= 2 && buf[bufLength - 2] == '\\') { 139 | if (bufLength >= 3 && buf[bufLength - 3] == '^') { 140 | buf[bufLength - 3] = '\\'; // replace "^\\\n" -> "\\\\\n" 141 | bufLength -= 2; // remove "\\\n" 142 | } else if (bufLength >= 3 && buf[bufLength - 3] == '\\') { 143 | bufLength--; // remove trailing \n 144 | } else { 145 | bufLength -= 2; // remove "\\\n" 146 | line.continuation = LineContinuationType::Backslash; 147 | } 148 | } else if (bufLength >= 2 && buf[bufLength - 2] == '^') { 149 | bufLength -= 2; 150 | line.continuation = LineContinuationType::Caret; 151 | } else { 152 | bufLength--; // remove trailing \n 153 | } 154 | 155 | line.content = QString::fromLatin1(buf, bufLength); 156 | return line; 157 | } 158 | 159 | /** 160 | * readLine implementation for unicode files. 161 | * Much slower than the 8 bit version. 162 | */ 163 | MakefileLine MakefileLineReader::readLine_impl_unicode() 164 | { 165 | MakefileLine line; 166 | QString str; 167 | do { 168 | m_nLineNumber++; 169 | str = m_textStream.readLine(); 170 | } while (str.startsWith(QLatin1Char('#'))); 171 | 172 | if (str.isNull()) 173 | return line; 174 | 175 | if (str.endsWith(QLatin1String("^\\"))) { 176 | str.remove(str.length() - 2, 1); 177 | } else if (str.endsWith(QLatin1String("\\\\"))) { 178 | // Do nothing. Double backslash at the end is not altered. 179 | } else if (str.endsWith(QLatin1Char('\\'))) { 180 | str.chop(1); 181 | line.continuation = LineContinuationType::Backslash; 182 | } else if (str.endsWith(QLatin1Char('^'))) { 183 | str.chop(1); 184 | line.continuation = LineContinuationType::Caret; 185 | } 186 | 187 | line.content = str; 188 | return line; 189 | } 190 | 191 | } // namespace NMakeFile 192 | -------------------------------------------------------------------------------- /src/jomlib/makefilelinereader.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of jom. 7 | ** 8 | ** Commercial License Usage 9 | ** Licensees holding valid commercial Qt licenses may use this file in 10 | ** accordance with the commercial license agreement provided with the 11 | ** Software or, alternatively, in accordance with the terms contained in 12 | ** a written agreement between you and The Qt Company. For licensing terms 13 | ** and conditions see https://www.qt.io/terms-conditions. For further 14 | ** information use the contact form at https://www.qt.io/contact-us. 15 | ** 16 | ** GNU General Public License Usage 17 | ** Alternatively, this file may be used under the terms of the GNU 18 | ** General Public License version 3 as published by the Free Software 19 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 20 | ** included in the packaging of this file. Please review the following 21 | ** information to ensure the GNU General Public License requirements will 22 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. 23 | ** 24 | ****************************************************************************/ 25 | 26 | #ifndef MAKEFILELINEREADER_H 27 | #define MAKEFILELINEREADER_H 28 | 29 | #include 30 | #include 31 | 32 | namespace NMakeFile { 33 | 34 | enum class LineContinuationType 35 | { 36 | None, 37 | Backslash, 38 | Caret 39 | }; 40 | 41 | struct MakefileLine 42 | { 43 | QString content; 44 | LineContinuationType continuation = LineContinuationType::None; 45 | }; 46 | 47 | inline bool isComplete(const MakefileLine &line) 48 | { 49 | return line.continuation == LineContinuationType::None; 50 | } 51 | 52 | class MakefileLineReader { 53 | public: 54 | MakefileLineReader(const QString& filename); 55 | ~MakefileLineReader(); 56 | 57 | bool open(); 58 | void close(); 59 | MakefileLine readLine(bool bInlineFileMode); 60 | QString fileName() const { return m_file.fileName(); } 61 | uint lineNumber() const { return m_nLineNumber; } 62 | 63 | private: 64 | void growLineBuffer(size_t nGrow); 65 | 66 | typedef MakefileLine (MakefileLineReader::*ReadLineImpl)(); 67 | ReadLineImpl m_readLineImpl; 68 | MakefileLine readLine_impl_local8bit(); 69 | MakefileLine readLine_impl_unicode(); 70 | 71 | private: 72 | QFile m_file; 73 | QTextStream m_textStream; 74 | static const size_t m_nInitialLineBufferSize = 6144; 75 | static const size_t m_nLineBufferGrowSize = 1024; 76 | size_t m_nLineBufferSize; 77 | char *m_lineBuffer; 78 | uint m_nLineNumber; 79 | }; 80 | 81 | } // namespace NMakeFile 82 | 83 | #endif // MAKEFILELINEREADER_H 84 | -------------------------------------------------------------------------------- /src/jomlib/options.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of jom. 7 | ** 8 | ** Commercial License Usage 9 | ** Licensees holding valid commercial Qt licenses may use this file in 10 | ** accordance with the commercial license agreement provided with the 11 | ** Software or, alternatively, in accordance with the terms contained in 12 | ** a written agreement between you and The Qt Company. For licensing terms 13 | ** and conditions see https://www.qt.io/terms-conditions. For further 14 | ** information use the contact form at https://www.qt.io/contact-us. 15 | ** 16 | ** GNU General Public License Usage 17 | ** Alternatively, this file may be used under the terms of the GNU 18 | ** General Public License version 3 as published by the Free Software 19 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 20 | ** included in the packaging of this file. Please review the following 21 | ** information to ensure the GNU General Public License requirements will 22 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. 23 | ** 24 | ****************************************************************************/ 25 | 26 | #ifndef OPTIONS_H 27 | #define OPTIONS_H 28 | 29 | #include 30 | #include 31 | 32 | namespace NMakeFile { 33 | 34 | class MacroTable; 35 | 36 | class Options 37 | { 38 | public: 39 | Options(); 40 | 41 | bool readCommandLineArguments(QStringList arguments, QString& makefile, 42 | QStringList& targets, MacroTable& macroTable); 43 | 44 | bool buildAllTargets; 45 | bool buildIfTimeStampsAreEqual; 46 | bool showLogo; 47 | bool suppressOutputMessages; 48 | bool overrideEnvVarMacros; 49 | bool displayIncludeFileNames; 50 | bool dryRun; 51 | bool stopOnErrors; 52 | bool buildUnrelatedTargetsOnError; 53 | bool checkTimeStampsButDoNotBuild; 54 | bool changeTimeStampsButDoNotBuild; 55 | bool ignorePredefinedRulesAndMacros; 56 | bool suppressExecutedCommandsDisplay; 57 | bool printWorkingDir; 58 | bool batchModeEnabled; 59 | bool dumpInlineFiles; 60 | bool dumpDependencyGraph; 61 | bool dumpDependencyGraphDot; 62 | bool displayMakeInformation; 63 | bool showUsageAndExit; 64 | bool displayBuildInfo; 65 | bool debugMode; 66 | bool showVersionAndExit; 67 | QString fullAppPath; 68 | QString stderrFile; 69 | 70 | private: 71 | bool expandCommandFiles(QStringList& arguments); 72 | bool handleCommandLineOption(const QStringList &originalArguments, QString arg, QStringList& arguments, QString& makefile, QString& makeflags); 73 | }; 74 | 75 | class GlobalOptions 76 | { 77 | public: 78 | GlobalOptions(); 79 | int maxNumberOfJobs; 80 | bool isMaxNumberOfJobsSet; 81 | }; 82 | 83 | extern GlobalOptions g_options; 84 | 85 | } // namespace NMakeFile 86 | 87 | #endif // OPTIONS_H 88 | -------------------------------------------------------------------------------- /src/jomlib/parser.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of jom. 7 | ** 8 | ** Commercial License Usage 9 | ** Licensees holding valid commercial Qt licenses may use this file in 10 | ** accordance with the commercial license agreement provided with the 11 | ** Software or, alternatively, in accordance with the terms contained in 12 | ** a written agreement between you and The Qt Company. For licensing terms 13 | ** and conditions see https://www.qt.io/terms-conditions. For further 14 | ** information use the contact form at https://www.qt.io/contact-us. 15 | ** 16 | ** GNU General Public License Usage 17 | ** Alternatively, this file may be used under the terms of the GNU 18 | ** General Public License version 3 as published by the Free Software 19 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 20 | ** included in the packaging of this file. Please review the following 21 | ** information to ensure the GNU General Public License requirements will 22 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. 23 | ** 24 | ****************************************************************************/ 25 | 26 | #ifndef PARSER_H 27 | #define PARSER_H 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #include "makefile.h" 36 | 37 | namespace NMakeFile { 38 | 39 | class Preprocessor; 40 | class PPExpression; 41 | 42 | class Parser 43 | { 44 | public: 45 | Parser(); 46 | virtual ~Parser(); 47 | 48 | void apply(Preprocessor* pp, 49 | Makefile* mkfile, 50 | const QStringList& activeTargets = QStringList()); 51 | MacroTable* macroTable(); 52 | 53 | private: 54 | void readLine(); 55 | bool isEmptyLine(const QString& line); 56 | bool isDescriptionBlock(int& separatorPos, int& separatorLength, int& commandSeparatorPos); 57 | bool isInferenceRule(const QString& line); 58 | bool isDotDirective(const QString& line); 59 | DescriptionBlock* createTarget(const QString& targetName); 60 | void parseDescriptionBlock(int separatorPos, int separatorLength, int commandSeparatorPos); 61 | void parseInferenceRule(); 62 | void parseDotDirective(); 63 | bool parseCommand(QList& commands, bool inferenceRule); 64 | void parseCommandLine(const QString& cmdLine, QList& commands, bool inferenceRule); 65 | void parseInlineFiles(Command& cmd, bool inferenceRule); 66 | void checkForCycles(DescriptionBlock* target); 67 | void resetCycleChecker(DescriptionBlock* target); 68 | QVector findRulesByTargetName(const QString& targetFilePath); 69 | void preselectInferenceRules(DescriptionBlock *target); 70 | void error(const QString& msg); 71 | 72 | private: 73 | Preprocessor* m_preprocessor; 74 | QString m_line; 75 | bool m_silentCommands; 76 | bool m_ignoreExitCodes; 77 | 78 | QRegExp m_rexDotDirective; 79 | QRegExp m_rexInferenceRule; 80 | QRegExp m_rexSingleWhiteSpace; 81 | 82 | Makefile* m_makefile; 83 | QStringList m_suffixes; 84 | QStringList m_activeTargets; 85 | QHash m_syncPoints; 86 | QHash > m_ruleIdxByToExtension; 87 | }; 88 | 89 | } // namespace NMakeFile 90 | 91 | #endif // PARSER_H 92 | -------------------------------------------------------------------------------- /src/jomlib/ppexpr.g: -------------------------------------------------------------------------------- 1 | ---------------------------------------------------------------------------- 2 | -- 3 | -- Copyright (C) 2016 The Qt Company Ltd. 4 | -- Contact: https://www.qt.io/licensing/ 5 | -- 6 | -- This file is part of jom. 7 | -- 8 | -- Commercial License Usage 9 | -- Licensees holding valid commercial Qt licenses may use this file in 10 | -- accordance with the commercial license agreement provided with the 11 | -- Software or, alternatively, in accordance with the terms contained in 12 | -- a written agreement between you and The Qt Company. For licensing terms 13 | -- and conditions see https://www.qt.io/terms-conditions. For further 14 | -- information use the contact form at https://www.qt.io/contact-us. 15 | -- 16 | -- GNU General Public License Usage 17 | -- Alternatively, this file may be used under the terms of the GNU 18 | -- General Public License version 3 as published by the Free Software 19 | -- Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 20 | -- included in the packaging of this file. Please review the following 21 | -- information to ensure the GNU General Public License requirements will 22 | -- be met: https://www.gnu.org/licenses/gpl-3.0.html. 23 | -- 24 | ---------------------------------------------------------------------------- 25 | 26 | %parser ppexpr_grammar 27 | %decl ppexprparser.h 28 | %impl ppexprparser.cpp 29 | 30 | %token_prefix T_ 31 | 32 | %token NUMBER 33 | %token STRING 34 | %token BOOL_AND 35 | %token BOOL_OR 36 | %token BIT_AND 37 | %token BIT_OR 38 | %token EQUAL 39 | %token NOT_EQUAL 40 | %token LESS_THAN 41 | %token GREATER_THAN 42 | %token EQUAL_OR_LESS_THAN 43 | %token EQUAL_OR_GREATER_THAN 44 | %token SHIFT_LEFT 45 | %token SHIFT_RIGHT 46 | %token PLUS 47 | %token MINUS 48 | %token MULT 49 | %token DIV 50 | %token MOD 51 | %token BIT_NOT 52 | %token BOOL_NOT 53 | %token LEFT_PAREN 54 | %token RIGHT_PAREN 55 | %token LINEFEED 56 | 57 | %start expression 58 | 59 | /: 60 | #include "ppexpr_grammar_p.h" 61 | #include 62 | 63 | namespace NMakeFile { 64 | class MacroTable; 65 | } 66 | 67 | class PPExprParser : protected $table 68 | { 69 | public: 70 | PPExprParser(); 71 | ~PPExprParser(); 72 | 73 | bool parse(const char* str); 74 | 75 | int expressionValue() 76 | { 77 | return sym(1).num; 78 | } 79 | 80 | QByteArray errorMessage() const 81 | { 82 | return m_errorMessage; 83 | } 84 | 85 | void setMacroTable(NMakeFile::MacroTable* macroTable) 86 | { 87 | m_macroTable = macroTable; 88 | } 89 | 90 | protected: 91 | struct Value 92 | { 93 | #ifdef _DEBUG 94 | Value() 95 | : num(0) 96 | {} 97 | #endif 98 | 99 | union { 100 | int num; 101 | QByteArray* str; 102 | }; 103 | }; 104 | 105 | protected: 106 | int yylex(); 107 | inline void reallocateStack(); 108 | 109 | inline Value &sym(int index) 110 | { return sym_stack [tos + index - 1]; } 111 | 112 | protected: 113 | void* yyInputBuffer; 114 | Value yylval; 115 | int tos; 116 | int stack_size; 117 | Value *sym_stack; 118 | int *state_stack; 119 | NMakeFile::MacroTable* m_macroTable; 120 | QByteArray m_errorMessage; 121 | }; 122 | :/ 123 | 124 | /. 125 | #include "ppexprparser.h" 126 | #include "macrotable.h" 127 | #include "ppexpr-lex.inc" 128 | 129 | PPExprParser::PPExprParser() 130 | : stack_size(0), 131 | sym_stack(0), 132 | state_stack(0), 133 | m_macroTable(0) 134 | { 135 | } 136 | 137 | PPExprParser::~PPExprParser() 138 | { 139 | if (stack_size) { 140 | free(sym_stack); 141 | free(state_stack); 142 | } 143 | } 144 | 145 | inline void PPExprParser::reallocateStack() 146 | { 147 | if (!stack_size) 148 | stack_size = 128; 149 | else 150 | stack_size <<= 1; 151 | 152 | sym_stack = reinterpret_cast (realloc(sym_stack, stack_size * sizeof(Value))); 153 | state_stack = reinterpret_cast (realloc(state_stack, stack_size * sizeof(int))); 154 | } 155 | 156 | bool PPExprParser::parse(const char* str) 157 | { 158 | const int INITIAL_STATE = 0; 159 | int yytoken = -1; 160 | 161 | if (!state_stack) 162 | reallocateStack(); 163 | 164 | tos = 0; 165 | m_errorMessage.clear(); 166 | state_stack[++tos] = INITIAL_STATE; 167 | yyInputBuffer = yy_scan_string(str); 168 | if (!yyInputBuffer) { 169 | m_errorMessage = "Can't create lexer's input buffer."; 170 | return false; 171 | } 172 | 173 | while (true) 174 | { 175 | if (yytoken == -1 && - TERMINAL_COUNT != action_index [state_stack [tos]]) { 176 | yytoken = yylex(); 177 | #ifdef DEBUG_GRAMMAR 178 | printf("*** yylex %d\n", yytoken); 179 | #endif 180 | } 181 | 182 | int act = t_action(state_stack [tos], yytoken); 183 | 184 | if (act == ACCEPT_STATE) { 185 | yy_delete_buffer((YY_BUFFER_STATE)yyInputBuffer); 186 | return true; 187 | } 188 | 189 | else if (act > 0) { 190 | if (++tos == stack_size) { 191 | reallocateStack(); 192 | if (!state_stack) { 193 | m_errorMessage = "stack overflow"; 194 | yy_delete_buffer((YY_BUFFER_STATE)yyInputBuffer); 195 | return false; 196 | } 197 | } 198 | 199 | sym_stack [tos] = yylval; 200 | state_stack [tos] = act; 201 | yytoken = -1; 202 | } 203 | 204 | else if (act < 0) 205 | { 206 | int r = - act - 1; 207 | 208 | #ifdef DEBUG_GRAMMAR 209 | int ridx = rule_index [r]; 210 | printf ("*** reduce using rule %d %s ::=", r + 1, spell[rule_info [ridx]]); 211 | ++ridx; 212 | for (int i = ridx; i < ridx + rhs [r]; ++i) 213 | { 214 | int symbol = rule_info [i]; 215 | if (const char *name = spell [symbol]) 216 | printf (" %s", name); 217 | else 218 | printf (" #%d", symbol); 219 | } 220 | printf ("\n"); 221 | #endif 222 | 223 | tos -= rhs [r]; 224 | act = state_stack [tos++]; 225 | 226 | switch (r) { 227 | ./ 228 | 229 | expression ::= term0; 230 | term0 ::= term1; 231 | term0 ::= term0 BOOL_AND term1; 232 | /. 233 | case $rule_number: // $rule 234 | sym(1).num = (sym(1).num != 0 && sym(3).num != 0) ? 1 : 0; 235 | break; 236 | ./ 237 | 238 | term0 ::= term0 BOOL_OR term1; 239 | /. 240 | case $rule_number: // $rule 241 | sym(1).num = (sym(1).num != 0 || sym(3).num != 0) ? 1 : 0; 242 | break; 243 | ./ 244 | 245 | term0 ::= term0 BIT_AND term1; 246 | /. 247 | case $rule_number: // $rule 248 | sym(1).num &= sym(3).num; 249 | break; 250 | ./ 251 | 252 | term0 ::= term0 BIT_OR term1; 253 | /. 254 | case $rule_number: // $rule 255 | sym(1).num |= sym(3).num; 256 | break; 257 | ./ 258 | 259 | term1 ::= term2; 260 | term1 ::= strterm1; 261 | 262 | strterm1 ::= STRING EQUAL STRING; 263 | /. 264 | case $rule_number: { // $rule 265 | QByteArray* lhs = sym(1).str; 266 | QByteArray* rhs = sym(3).str; 267 | sym(1).num = (*lhs == *rhs) ? 1 : 0; 268 | delete lhs; 269 | delete rhs; 270 | break; 271 | } 272 | ./ 273 | 274 | strterm1 ::= STRING NOT_EQUAL STRING; 275 | /. 276 | case $rule_number: { // $rule 277 | QByteArray* lhs = sym(1).str; 278 | QByteArray* rhs = sym(3).str; 279 | sym(1).num = (*lhs == *rhs) ? 0 : 1; 280 | delete lhs; 281 | delete rhs; 282 | break; 283 | } 284 | ./ 285 | 286 | term2 ::= term3; 287 | term2 ::= term2 EQUAL term3; 288 | /. 289 | case $rule_number: // $rule 290 | sym(1).num = (sym(1).num == sym(3).num); 291 | break; 292 | ./ 293 | 294 | term2 ::= term2 NOT_EQUAL term3; 295 | /. 296 | case $rule_number: // $rule 297 | sym(1).num = (sym(1).num != sym(3).num); 298 | break; 299 | ./ 300 | 301 | term2 ::= term2 LESS_THAN term3; 302 | /. 303 | case $rule_number: // $rule 304 | sym(1).num = (sym(1).num < sym(3).num); 305 | break; 306 | ./ 307 | 308 | term2 ::= term2 GREATER_THAN term3; 309 | /. 310 | case $rule_number: // $rule 311 | sym(1).num = (sym(1).num > sym(3).num); 312 | break; 313 | ./ 314 | 315 | term2 ::= term2 EQUAL_OR_LESS_THAN term3; 316 | /. 317 | case $rule_number: // $rule 318 | sym(1).num = (sym(1).num <= sym(3).num); 319 | break; 320 | ./ 321 | 322 | term2 ::= term2 EQUAL_OR_GREATER_THAN term3; 323 | /. 324 | case $rule_number: // $rule 325 | sym(1).num = (sym(1).num >= sym(3).num); 326 | break; 327 | ./ 328 | 329 | term3 ::= term4; 330 | term3 ::= term3 SHIFT_LEFT term4; 331 | /. 332 | case $rule_number: // $rule 333 | sym(1).num <<= sym(3).num; 334 | break; 335 | ./ 336 | 337 | term3 ::= term3 SHIFT_RIGHT term4; 338 | /. 339 | case $rule_number: // $rule 340 | sym(1).num >>= sym(3).num; 341 | break; 342 | ./ 343 | 344 | term4 ::= term5; 345 | term4 ::= term4 PLUS term5; 346 | /. 347 | case $rule_number: // $rule 348 | sym(1).num += sym(3).num; 349 | break; 350 | ./ 351 | 352 | term4 ::= term4 MINUS term5; 353 | /. 354 | case $rule_number: // $rule 355 | sym(1).num -= sym(3).num; 356 | break; 357 | ./ 358 | 359 | term5 ::= term6; 360 | term5 ::= term5 MULT term6; 361 | /. 362 | case $rule_number: // $rule 363 | sym(1).num *= sym(3).num; 364 | break; 365 | ./ 366 | 367 | term5 ::= term5 DIV term6; 368 | /. 369 | case $rule_number: { // $rule 370 | const int rhs = sym(3).num; 371 | if (rhs == 0) { 372 | m_errorMessage = "division by zero"; 373 | yy_delete_buffer((YY_BUFFER_STATE)yyInputBuffer); 374 | return false; 375 | } 376 | sym(1).num /= rhs; 377 | break; 378 | } 379 | ./ 380 | 381 | term5 ::= term5 MOD term6; 382 | /. 383 | case $rule_number: // $rule 384 | sym(1).num %= sym(3).num; 385 | break; 386 | ./ 387 | 388 | term6 ::= primary; 389 | term6 ::= MINUS primary; 390 | /. 391 | case $rule_number: // $rule 392 | sym(1).num = -sym(2).num; 393 | break; 394 | ./ 395 | 396 | term6 ::= BIT_NOT primary; 397 | /. 398 | case $rule_number: // $rule 399 | sym(1).num = ~sym(2).num; 400 | break; 401 | ./ 402 | 403 | term6 ::= BOOL_NOT primary; 404 | /. 405 | case $rule_number: // $rule 406 | sym(1).num = (sym(2).num == 0) ? 1 : 0; 407 | break; 408 | ./ 409 | 410 | primary ::= NUMBER; 411 | primary ::= LEFT_PAREN term0 RIGHT_PAREN; 412 | /. 413 | case $rule_number: // $rule 414 | sym(1).num = sym(2).num; 415 | break; 416 | ./ 417 | 418 | /. 419 | } // switch 420 | 421 | state_stack [tos] = nt_action (act, lhs [r] - TERMINAL_COUNT); 422 | } 423 | else 424 | { 425 | // ### ERROR RECOVERY HERE 426 | break; 427 | } 428 | } 429 | 430 | m_errorMessage = "Syntax Error"; 431 | yy_delete_buffer((YY_BUFFER_STATE)yyInputBuffer); 432 | return false; 433 | } 434 | ./ 435 | 436 | -------------------------------------------------------------------------------- /src/jomlib/ppexpr.l: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of jom. 7 | ** 8 | ** Commercial License Usage 9 | ** Licensees holding valid commercial Qt licenses may use this file in 10 | ** accordance with the commercial license agreement provided with the 11 | ** Software or, alternatively, in accordance with the terms contained in 12 | ** a written agreement between you and The Qt Company. For licensing terms 13 | ** and conditions see https://www.qt.io/terms-conditions. For further 14 | ** information use the contact form at https://www.qt.io/contact-us. 15 | ** 16 | ** GNU General Public License Usage 17 | ** Alternatively, this file may be used under the terms of the GNU 18 | ** General Public License version 3 as published by the Free Software 19 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 20 | ** included in the packaging of this file. Please review the following 21 | ** information to ensure the GNU General Public License requirements will 22 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. 23 | ** 24 | ****************************************************************************/ 25 | 26 | %option 8bit nounistd batch 27 | %option warn 28 | /*%option nodefault*/ 29 | %option outfile="ppexpr-lex.inc" 30 | /*%option header-file="ppexpr-lex.h"*/ 31 | 32 | %{ 33 | #define YY_DECL int PPExprParser::yylex() 34 | #define ECHO 35 | #define YY_SKIP_YYWRAP 36 | #define fileno _fileno 37 | 38 | static int isatty (int) 39 | { 40 | return 0; 41 | } 42 | 43 | static int yywrap() 44 | { 45 | return 1; 46 | } 47 | %} 48 | 49 | %x STATE_STRING 50 | %x STATE_DEFINED 51 | %x STATE_EXIST 52 | %x STATE_SHELLCOMMAND 53 | 54 | %% 55 | 56 | [ \t]+ /* eat whitespace */ 57 | [0-9]+ { 58 | yylval.num = atoi(yytext); 59 | return T_NUMBER; 60 | } 61 | \r?\n return T_LINEFEED; 62 | "&&" return T_BOOL_AND; 63 | "||" return T_BOOL_OR; 64 | "&" return T_BIT_AND; 65 | "|" return T_BIT_OR; 66 | "!=" return T_NOT_EQUAL; 67 | "==" return T_EQUAL; 68 | "<" return T_LESS_THAN; 69 | ">" return T_GREATER_THAN; 70 | "<=" return T_EQUAL_OR_LESS_THAN; 71 | ">=" return T_EQUAL_OR_GREATER_THAN; 72 | "<<" return T_SHIFT_LEFT; 73 | ">>" return T_SHIFT_RIGHT; 74 | "+" return T_PLUS; 75 | "-" return T_MINUS; 76 | "*" return T_MULT; 77 | "/" return T_DIV; 78 | "%" return T_MOD; 79 | "~" return T_BIT_NOT; 80 | "!" return T_BOOL_NOT; 81 | "(" return T_LEFT_PAREN; 82 | ")" return T_RIGHT_PAREN; 83 | 84 | \" { 85 | BEGIN(STATE_STRING); 86 | yylval.str = new QByteArray(); 87 | } 88 | 89 | "[" { 90 | BEGIN(STATE_SHELLCOMMAND); 91 | yylval.str = new QByteArray(); 92 | } 93 | 94 | (?i:DEFINED) BEGIN(STATE_DEFINED); 95 | (?i:EXIST) BEGIN(STATE_EXIST); 96 | 97 | { 98 | [:blank:]+ /* eat whitespace */ 99 | "("[^\)]+")" { 100 | BEGIN(INITIAL); 101 | QByteArray macroName(yytext); 102 | int idx = macroName.indexOf('('); 103 | macroName.remove(0, idx + 1); 104 | idx = macroName.lastIndexOf(')'); 105 | macroName.truncate(idx); 106 | macroName = macroName.trimmed(); 107 | if (macroName.startsWith('\"')) 108 | macroName.remove(0, 1); 109 | if (macroName.endsWith('\"')) 110 | macroName.chop(1); 111 | yylval.num = m_macroTable->isMacroDefined(QString::fromLatin1(macroName.constData())) ? 1 : 0; 112 | return T_NUMBER; 113 | } 114 | } 115 | 116 | { 117 | [:blank:]+ /* eat whitespace */ 118 | "(".+")" { 119 | BEGIN(INITIAL); 120 | QByteArray fileName(yytext); 121 | int idx = fileName.indexOf('('); 122 | fileName.remove(0, idx + 1); 123 | idx = fileName.lastIndexOf(')'); 124 | fileName.truncate(idx); 125 | fileName = fileName.trimmed(); 126 | if (fileName.startsWith('\"')) 127 | fileName.remove(0, 1); 128 | if (fileName.endsWith('\"')) 129 | fileName.chop(1); 130 | yylval.num = QFile::exists(QString::fromLatin1(fileName.constData())) ? 1 : 0; 131 | return T_NUMBER; 132 | } 133 | } 134 | 135 | "EXIST"[:blank:]*"(".*")" { 136 | QByteArray fileName(yytext); 137 | int idx = fileName.indexOf('('); 138 | fileName.remove(0, idx + 1); 139 | idx = fileName.lastIndexOf(')'); 140 | fileName.truncate(idx); 141 | fileName = fileName.trimmed(); 142 | if (fileName.startsWith('\"')) 143 | fileName.remove(0, 1); 144 | if (fileName.endsWith('\"')) 145 | fileName.chop(1); 146 | yylval.num = QFile::exists(QString::fromLatin1(fileName.constData())) ? 1 : 0; 147 | return T_NUMBER; 148 | } 149 | 150 | { 151 | [^\n\"]+ yylval.str->append(yytext, static_cast(yyleng)); 152 | \"\" yylval.str->append('\"'); 153 | \" { 154 | BEGIN(INITIAL); 155 | return T_STRING; 156 | } 157 | } 158 | 159 | { 160 | [^\n\x5b\x5d\x5e]+ yylval.str->append(yytext, static_cast(yyleng)); 161 | "^[" yylval.str->append('['); 162 | "^]" yylval.str->append(']'); 163 | "]" { 164 | BEGIN(INITIAL); 165 | int exitCode = system(yylval.str->data()); 166 | delete yylval.str; 167 | yylval.num = exitCode; 168 | return T_NUMBER; 169 | } 170 | } 171 | 172 | %% 173 | 174 | -------------------------------------------------------------------------------- /src/jomlib/ppexpr_grammar.cpp: -------------------------------------------------------------------------------- 1 | // This file was generated by qlalr - DO NOT EDIT! 2 | #include "ppexpr_grammar_p.h" 3 | 4 | const char *const ppexpr_grammar::spell [] = { 5 | "end of file", 0, 0, 0, 0, 0, 0, 0, 0, 0, 6 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7 | 0, 0, 0, 0, 0, 8 | #ifndef QLALR_NO_PPEXPR_GRAMMAR_DEBUG_INFO 9 | "expression", "term0", "term1", "term2", "strterm1", 10 | "term3", "term4", "term5", "term6", "primary", "$accept" 11 | #endif // QLALR_NO_PPEXPR_GRAMMAR_DEBUG_INFO 12 | }; 13 | 14 | const short ppexpr_grammar::lhs [] = { 15 | 25, 26, 26, 26, 26, 26, 27, 27, 29, 29, 16 | 28, 28, 28, 28, 28, 28, 28, 30, 30, 30, 17 | 31, 31, 31, 32, 32, 32, 32, 33, 33, 33, 18 | 33, 34, 34, 35}; 19 | 20 | const short ppexpr_grammar::rhs [] = { 21 | 1, 1, 3, 3, 3, 3, 1, 1, 3, 3, 22 | 1, 3, 3, 3, 3, 3, 3, 1, 3, 3, 23 | 1, 3, 3, 1, 3, 3, 3, 1, 2, 2, 24 | 2, 1, 3, 2}; 25 | 26 | 27 | #ifndef QLALR_NO_PPEXPR_GRAMMAR_DEBUG_INFO 28 | const int ppexpr_grammar::rule_info [] = { 29 | 25, 26 30 | , 26, 27 31 | , 26, 26, 3, 27 32 | , 26, 26, 4, 27 33 | , 26, 26, 5, 27 34 | , 26, 26, 6, 27 35 | , 27, 28 36 | , 27, 29 37 | , 29, 2, 7, 2 38 | , 29, 2, 8, 2 39 | , 28, 30 40 | , 28, 28, 7, 30 41 | , 28, 28, 8, 30 42 | , 28, 28, 9, 30 43 | , 28, 28, 10, 30 44 | , 28, 28, 11, 30 45 | , 28, 28, 12, 30 46 | , 30, 31 47 | , 30, 30, 13, 31 48 | , 30, 30, 14, 31 49 | , 31, 32 50 | , 31, 31, 15, 32 51 | , 31, 31, 16, 32 52 | , 32, 33 53 | , 32, 32, 17, 33 54 | , 32, 32, 18, 33 55 | , 32, 32, 19, 33 56 | , 33, 34 57 | , 33, 16, 34 58 | , 33, 20, 34 59 | , 33, 21, 34 60 | , 34, 1 61 | , 34, 22, 26, 23 62 | , 35, 25, 0}; 63 | 64 | const int ppexpr_grammar::rule_index [] = { 65 | 0, 2, 4, 8, 12, 16, 20, 22, 24, 28, 66 | 32, 34, 38, 42, 46, 50, 54, 58, 60, 64, 67 | 68, 70, 74, 78, 80, 84, 88, 92, 94, 97, 68 | 100, 103, 105, 109}; 69 | #endif // QLALR_NO_PPEXPR_GRAMMAR_DEBUG_INFO 70 | 71 | const short ppexpr_grammar::action_default [] = { 72 | 0, 0, 0, 0, 0, 32, 0, 0, 28, 8, 73 | 1, 2, 7, 11, 18, 21, 24, 30, 31, 0, 74 | 0, 0, 0, 0, 33, 5, 6, 3, 4, 29, 75 | 0, 0, 9, 10, 34, 0, 0, 0, 0, 0, 76 | 0, 12, 0, 0, 19, 0, 0, 23, 0, 0, 77 | 0, 26, 27, 25, 22, 20, 17, 16, 15, 14, 78 | 13}; 79 | 80 | const short ppexpr_grammar::goto_default [] = { 81 | 7, 10, 11, 12, 9, 13, 14, 15, 16, 8, 82 | 0}; 83 | 84 | const short ppexpr_grammar::action_index [] = { 85 | 117, 1, 2, 76, 5, -25, 3, 0, -25, -25, 86 | 45, -25, 113, 6, -8, 18, -25, -25, -25, 57, 87 | 110, 117, 85, 141, -25, -25, -25, -25, -25, -25, 88 | 23, -1, -25, -25, -25, 51, 93, 69, 25, 37, 89 | 69, 15, 69, 69, -3, 69, 93, 18, 69, 69, 90 | 69, -25, -25, -25, 18, 16, 20, 20, -9, 4, 91 | 8, 92 | 93 | -11, -9, -7, 3, -8, -11, -11, -11, -11, -11, 94 | -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, 95 | 1, 12, 4, 21, -11, -11, -11, -11, -11, -11, 96 | -11, -11, -11, -11, -11, 10, 15, 16, 0, 6, 97 | 7, -11, 11, 13, -11, 9, 17, -11, -1, 2, 98 | 5, -11, -11, -11, -11, -11, -11, -11, -11, -11, 99 | -11}; 100 | 101 | const short ppexpr_grammar::action_info [] = { 102 | 34, 33, 5, 5, 42, 43, 5, 46, 45, 0, 103 | 30, 31, 46, 45, 0, 0, 0, 42, 43, 42, 104 | 43, 42, 43, 3, 3, 32, 5, 3, 42, 43, 105 | 0, 46, 45, 42, 43, 50, 48, 49, 5, 0, 106 | 0, 4, 0, 0, 0, 1, 2, 3, 22, 23, 107 | 20, 21, 5, 4, 0, 0, 0, 1, 2, 3, 108 | 22, 23, 20, 21, 0, 0, 0, 4, 0, 0, 109 | 5, 1, 2, 3, 0, 0, 0, 5, 6, 0, 110 | 24, 0, 0, 0, 0, 4, 5, 6, 0, 1, 111 | 2, 3, 4, 0, 5, 0, 1, 2, 3, 0, 112 | 0, 4, 0, 0, 0, 1, 2, 3, 0, 4, 113 | 0, 5, 6, 1, 2, 3, 0, 0, 5, 6, 114 | 35, 40, 39, 38, 37, 36, 4, 0, 0, 0, 115 | 1, 2, 3, 4, 0, 0, 0, 1, 2, 3, 116 | 0, 0, 5, 6, 0, 0, 0, 0, 0, 0, 117 | 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 118 | 0, 1, 2, 3, 0, 0, 119 | 120 | 17, 29, 18, 25, 19, 58, 27, 51, 0, 0, 121 | 52, 59, 60, 53, 26, 41, 47, 44, 0, 55, 122 | 56, 57, 0, 28, 54, 0, 0, 0, 0, 0, 123 | 0, 0}; 124 | 125 | const short ppexpr_grammar::action_check [] = { 126 | 0, 2, 1, 1, 13, 14, 1, 15, 16, -1, 127 | 7, 8, 15, 16, -1, -1, -1, 13, 14, 13, 128 | 14, 13, 14, 22, 22, 2, 1, 22, 13, 14, 129 | -1, 15, 16, 13, 14, 17, 18, 19, 1, -1, 130 | -1, 16, -1, -1, -1, 20, 21, 22, 3, 4, 131 | 5, 6, 1, 16, -1, -1, -1, 20, 21, 22, 132 | 3, 4, 5, 6, -1, -1, -1, 16, -1, -1, 133 | 1, 20, 21, 22, -1, -1, -1, 1, 2, -1, 134 | 23, -1, -1, -1, -1, 16, 1, 2, -1, 20, 135 | 21, 22, 16, -1, 1, -1, 20, 21, 22, -1, 136 | -1, 16, -1, -1, -1, 20, 21, 22, -1, 16, 137 | -1, 1, 2, 20, 21, 22, -1, -1, 1, 2, 138 | 7, 8, 9, 10, 11, 12, 16, -1, -1, -1, 139 | 20, 21, 22, 16, -1, -1, -1, 20, 21, 22, 140 | -1, -1, 1, 2, -1, -1, -1, -1, -1, -1, 141 | -1, -1, -1, -1, -1, -1, -1, 16, -1, -1, 142 | -1, 20, 21, 22, -1, -1, 143 | 144 | 9, 9, 9, 2, 1, 5, 2, 8, -1, -1, 145 | 8, 5, 5, 8, 2, 5, 7, 6, -1, 6, 146 | 5, 5, -1, 2, 7, -1, -1, -1, -1, -1, 147 | -1, -1}; 148 | 149 | -------------------------------------------------------------------------------- /src/jomlib/ppexpr_grammar_p.h: -------------------------------------------------------------------------------- 1 | // This file was generated by qlalr - DO NOT EDIT! 2 | #ifndef PPEXPR_GRAMMAR_P_H 3 | #define PPEXPR_GRAMMAR_P_H 4 | 5 | class ppexpr_grammar 6 | { 7 | public: 8 | enum VariousConstants { 9 | EOF_SYMBOL = 0, 10 | T_BIT_AND = 5, 11 | T_BIT_NOT = 20, 12 | T_BIT_OR = 6, 13 | T_BOOL_AND = 3, 14 | T_BOOL_NOT = 21, 15 | T_BOOL_OR = 4, 16 | T_DIV = 18, 17 | T_EQUAL = 7, 18 | T_EQUAL_OR_GREATER_THAN = 12, 19 | T_EQUAL_OR_LESS_THAN = 11, 20 | T_GREATER_THAN = 10, 21 | T_LEFT_PAREN = 22, 22 | T_LESS_THAN = 9, 23 | T_LINEFEED = 24, 24 | T_MINUS = 16, 25 | T_MOD = 19, 26 | T_MULT = 17, 27 | T_NOT_EQUAL = 8, 28 | T_NUMBER = 1, 29 | T_PLUS = 15, 30 | T_RIGHT_PAREN = 23, 31 | T_SHIFT_LEFT = 13, 32 | T_SHIFT_RIGHT = 14, 33 | T_STRING = 2, 34 | 35 | ACCEPT_STATE = 34, 36 | RULE_COUNT = 34, 37 | STATE_COUNT = 61, 38 | TERMINAL_COUNT = 25, 39 | NON_TERMINAL_COUNT = 11, 40 | 41 | GOTO_INDEX_OFFSET = 61, 42 | GOTO_INFO_OFFSET = 166, 43 | GOTO_CHECK_OFFSET = 166 44 | }; 45 | 46 | static const char *const spell []; 47 | static const short lhs []; 48 | static const short rhs []; 49 | 50 | #ifndef QLALR_NO_PPEXPR_GRAMMAR_DEBUG_INFO 51 | static const int rule_index []; 52 | static const int rule_info []; 53 | #endif // QLALR_NO_PPEXPR_GRAMMAR_DEBUG_INFO 54 | 55 | static const short goto_default []; 56 | static const short action_default []; 57 | static const short action_index []; 58 | static const short action_info []; 59 | static const short action_check []; 60 | 61 | static inline int nt_action (int state, int nt) 62 | { 63 | const int yyn = action_index [GOTO_INDEX_OFFSET + state] + nt; 64 | if (yyn < 0 || action_check [GOTO_CHECK_OFFSET + yyn] != nt) 65 | return goto_default [nt]; 66 | 67 | return action_info [GOTO_INFO_OFFSET + yyn]; 68 | } 69 | 70 | static inline int t_action (int state, int token) 71 | { 72 | const int yyn = action_index [state] + token; 73 | 74 | if (yyn < 0 || action_check [yyn] != token) 75 | return - action_default [state]; 76 | 77 | return action_info [yyn]; 78 | } 79 | }; 80 | 81 | 82 | #endif // PPEXPR_GRAMMAR_P_H 83 | 84 | -------------------------------------------------------------------------------- /src/jomlib/ppexprparser.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of jom. 7 | ** 8 | ** Commercial License Usage 9 | ** Licensees holding valid commercial Qt licenses may use this file in 10 | ** accordance with the commercial license agreement provided with the 11 | ** Software or, alternatively, in accordance with the terms contained in 12 | ** a written agreement between you and The Qt Company. For licensing terms 13 | ** and conditions see https://www.qt.io/terms-conditions. For further 14 | ** information use the contact form at https://www.qt.io/contact-us. 15 | ** 16 | ** GNU General Public License Usage 17 | ** Alternatively, this file may be used under the terms of the GNU 18 | ** General Public License version 3 as published by the Free Software 19 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 20 | ** included in the packaging of this file. Please review the following 21 | ** information to ensure the GNU General Public License requirements will 22 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. 23 | ** 24 | ****************************************************************************/ 25 | 26 | #include "ppexprparser.h" 27 | #include "macrotable.h" 28 | #include "ppexpr-lex.inc" 29 | 30 | PPExprParser::PPExprParser() 31 | : stack_size(0), 32 | sym_stack(0), 33 | state_stack(0), 34 | m_macroTable(0) 35 | { 36 | } 37 | 38 | PPExprParser::~PPExprParser() 39 | { 40 | if (stack_size) { 41 | free(sym_stack); 42 | free(state_stack); 43 | } 44 | } 45 | 46 | inline void PPExprParser::reallocateStack() 47 | { 48 | if (!stack_size) 49 | stack_size = 128; 50 | else 51 | stack_size <<= 1; 52 | 53 | sym_stack = reinterpret_cast (realloc(sym_stack, stack_size * sizeof(Value))); 54 | state_stack = reinterpret_cast (realloc(state_stack, stack_size * sizeof(int))); 55 | } 56 | 57 | bool PPExprParser::parse(const char* str) 58 | { 59 | const int INITIAL_STATE = 0; 60 | int yytoken = -1; 61 | 62 | if (!state_stack) 63 | reallocateStack(); 64 | 65 | tos = 0; 66 | m_errorMessage.clear(); 67 | state_stack[++tos] = INITIAL_STATE; 68 | yyInputBuffer = yy_scan_string(str); 69 | if (!yyInputBuffer) { 70 | m_errorMessage = "Can't create lexer's input buffer."; 71 | return false; 72 | } 73 | 74 | while (true) 75 | { 76 | if (yytoken == -1 && - TERMINAL_COUNT != action_index [state_stack [tos]]) { 77 | yytoken = yylex(); 78 | #ifdef DEBUG_GRAMMAR 79 | printf("*** yylex %d\n", yytoken); 80 | #endif 81 | } 82 | 83 | int act = t_action(state_stack [tos], yytoken); 84 | 85 | if (act == ACCEPT_STATE) { 86 | yy_delete_buffer((YY_BUFFER_STATE)yyInputBuffer); 87 | return true; 88 | } 89 | 90 | else if (act > 0) { 91 | if (++tos == stack_size) { 92 | reallocateStack(); 93 | if (!state_stack) { 94 | m_errorMessage = "stack overflow"; 95 | yy_delete_buffer((YY_BUFFER_STATE)yyInputBuffer); 96 | return false; 97 | } 98 | } 99 | 100 | sym_stack [tos] = yylval; 101 | state_stack [tos] = act; 102 | yytoken = -1; 103 | } 104 | 105 | else if (act < 0) 106 | { 107 | int r = - act - 1; 108 | 109 | #ifdef DEBUG_GRAMMAR 110 | int ridx = rule_index [r]; 111 | printf ("*** reduce using rule %d %s ::=", r + 1, spell[rule_info [ridx]]); 112 | ++ridx; 113 | for (int i = ridx; i < ridx + rhs [r]; ++i) 114 | { 115 | int symbol = rule_info [i]; 116 | if (const char *name = spell [symbol]) 117 | printf (" %s", name); 118 | else 119 | printf (" #%d", symbol); 120 | } 121 | printf ("\n"); 122 | #endif 123 | 124 | tos -= rhs [r]; 125 | act = state_stack [tos++]; 126 | 127 | switch (r) { 128 | 129 | case 2: // term0 130 | sym(1).num = (sym(1).num != 0 && sym(3).num != 0) ? 1 : 0; 131 | break; 132 | 133 | case 3: // term0 134 | sym(1).num = (sym(1).num != 0 || sym(3).num != 0) ? 1 : 0; 135 | break; 136 | 137 | case 4: // term0 138 | sym(1).num &= sym(3).num; 139 | break; 140 | 141 | case 5: // term0 142 | sym(1).num |= sym(3).num; 143 | break; 144 | 145 | case 8: { // strterm1 146 | QByteArray* lhs = sym(1).str; 147 | QByteArray* rhs = sym(3).str; 148 | sym(1).num = (*lhs == *rhs) ? 1 : 0; 149 | delete lhs; 150 | delete rhs; 151 | break; 152 | } 153 | 154 | case 9: { // strterm1 155 | QByteArray* lhs = sym(1).str; 156 | QByteArray* rhs = sym(3).str; 157 | sym(1).num = (*lhs == *rhs) ? 0 : 1; 158 | delete lhs; 159 | delete rhs; 160 | break; 161 | } 162 | 163 | case 11: // term2 164 | sym(1).num = (sym(1).num == sym(3).num); 165 | break; 166 | 167 | case 12: // term2 168 | sym(1).num = (sym(1).num != sym(3).num); 169 | break; 170 | 171 | case 13: // term2 172 | sym(1).num = (sym(1).num < sym(3).num); 173 | break; 174 | 175 | case 14: // term2 176 | sym(1).num = (sym(1).num > sym(3).num); 177 | break; 178 | 179 | case 15: // term2 180 | sym(1).num = (sym(1).num <= sym(3).num); 181 | break; 182 | 183 | case 16: // term2 184 | sym(1).num = (sym(1).num >= sym(3).num); 185 | break; 186 | 187 | case 18: // term3 188 | sym(1).num <<= sym(3).num; 189 | break; 190 | 191 | case 19: // term3 192 | sym(1).num >>= sym(3).num; 193 | break; 194 | 195 | case 21: // term4 196 | sym(1).num += sym(3).num; 197 | break; 198 | 199 | case 22: // term4 200 | sym(1).num -= sym(3).num; 201 | break; 202 | 203 | case 24: // term5 204 | sym(1).num *= sym(3).num; 205 | break; 206 | 207 | case 25: { // term5 208 | const int rhs = sym(3).num; 209 | if (rhs == 0) { 210 | m_errorMessage = "division by zero"; 211 | yy_delete_buffer((YY_BUFFER_STATE)yyInputBuffer); 212 | return false; 213 | } 214 | sym(1).num /= rhs; 215 | break; 216 | } 217 | 218 | case 26: // term5 219 | sym(1).num %= sym(3).num; 220 | break; 221 | 222 | case 28: // term6 223 | sym(1).num = -sym(2).num; 224 | break; 225 | 226 | case 29: // term6 227 | sym(1).num = ~sym(2).num; 228 | break; 229 | 230 | case 30: // term6 231 | sym(1).num = (sym(2).num == 0) ? 1 : 0; 232 | break; 233 | 234 | case 32: // primary 235 | sym(1).num = sym(2).num; 236 | break; 237 | 238 | } // switch 239 | 240 | state_stack [tos] = nt_action (act, lhs [r] - TERMINAL_COUNT); 241 | } 242 | else 243 | { 244 | // ### ERROR RECOVERY HERE 245 | break; 246 | } 247 | } 248 | 249 | m_errorMessage = "Syntax Error"; 250 | yy_delete_buffer((YY_BUFFER_STATE)yyInputBuffer); 251 | return false; 252 | } 253 | -------------------------------------------------------------------------------- /src/jomlib/ppexprparser.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of jom. 7 | ** 8 | ** Commercial License Usage 9 | ** Licensees holding valid commercial Qt licenses may use this file in 10 | ** accordance with the commercial license agreement provided with the 11 | ** Software or, alternatively, in accordance with the terms contained in 12 | ** a written agreement between you and The Qt Company. For licensing terms 13 | ** and conditions see https://www.qt.io/terms-conditions. For further 14 | ** information use the contact form at https://www.qt.io/contact-us. 15 | ** 16 | ** GNU General Public License Usage 17 | ** Alternatively, this file may be used under the terms of the GNU 18 | ** General Public License version 3 as published by the Free Software 19 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 20 | ** included in the packaging of this file. Please review the following 21 | ** information to ensure the GNU General Public License requirements will 22 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. 23 | ** 24 | ****************************************************************************/ 25 | 26 | #include "ppexpr_grammar_p.h" 27 | #include 28 | 29 | namespace NMakeFile { 30 | class MacroTable; 31 | } 32 | 33 | class PPExprParser : protected ppexpr_grammar 34 | { 35 | public: 36 | PPExprParser(); 37 | ~PPExprParser(); 38 | 39 | bool parse(const char* str); 40 | 41 | int expressionValue() 42 | { 43 | return sym(1).num; 44 | } 45 | 46 | QByteArray errorMessage() const 47 | { 48 | return m_errorMessage; 49 | } 50 | 51 | void setMacroTable(NMakeFile::MacroTable* macroTable) 52 | { 53 | m_macroTable = macroTable; 54 | } 55 | 56 | protected: 57 | struct Value 58 | { 59 | #ifdef _DEBUG 60 | Value() 61 | : num(0) 62 | {} 63 | #endif 64 | 65 | union { 66 | int num; 67 | QByteArray* str; 68 | }; 69 | }; 70 | 71 | protected: 72 | int yylex(); 73 | inline void reallocateStack(); 74 | 75 | inline Value &sym(int index) 76 | { return sym_stack [tos + index - 1]; } 77 | 78 | protected: 79 | void* yyInputBuffer; 80 | Value yylval; 81 | int tos; 82 | int stack_size; 83 | Value *sym_stack; 84 | int *state_stack; 85 | NMakeFile::MacroTable* m_macroTable; 86 | QByteArray m_errorMessage; 87 | }; 88 | -------------------------------------------------------------------------------- /src/jomlib/preprocessor.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of jom. 7 | ** 8 | ** Commercial License Usage 9 | ** Licensees holding valid commercial Qt licenses may use this file in 10 | ** accordance with the commercial license agreement provided with the 11 | ** Software or, alternatively, in accordance with the terms contained in 12 | ** a written agreement between you and The Qt Company. For licensing terms 13 | ** and conditions see https://www.qt.io/terms-conditions. For further 14 | ** information use the contact form at https://www.qt.io/contact-us. 15 | ** 16 | ** GNU General Public License Usage 17 | ** Alternatively, this file may be used under the terms of the GNU 18 | ** General Public License version 3 as published by the Free Software 19 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 20 | ** included in the packaging of this file. Please review the following 21 | ** information to ensure the GNU General Public License requirements will 22 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. 23 | ** 24 | ****************************************************************************/ 25 | 26 | #ifndef PREPROCESSOR_H 27 | #define PREPROCESSOR_H 28 | 29 | #include "makefilelinereader.h" 30 | 31 | #include 32 | #include 33 | #include 34 | 35 | class PPExprParser; 36 | 37 | namespace NMakeFile { 38 | 39 | class MacroTable; 40 | class MakefileLineReader; 41 | 42 | class Preprocessor 43 | { 44 | public: 45 | Preprocessor(); 46 | ~Preprocessor(); 47 | 48 | void setMacroTable(MacroTable* macroTable); 49 | MacroTable* macroTable() { return m_macroTable; } 50 | bool openFile(const QString& filename); 51 | QString readLine(); 52 | uint lineNumber() const; 53 | QString currentFileName() const; 54 | int evaluateExpression(const QString& expr); 55 | bool isInlineFileMode() const { return m_bInlineFileMode; } 56 | void setInlineFileModeEnabled(bool enabled) { m_bInlineFileMode = enabled; } 57 | 58 | static void removeInlineComments(QString& line); 59 | 60 | private: 61 | bool internalOpenFile(QString fileName); 62 | MakefileLine basicReadLine(); 63 | typedef void (*JoinFunc)(MakefileLine &, const MakefileLine &); 64 | static void joinLines(MakefileLine &line, const MakefileLine &next); 65 | static void joinPreprocessingDirectiveLines(MakefileLine &line, const MakefileLine &next); 66 | void completeLineImpl(MakefileLine &line, JoinFunc joinFunc); 67 | void completeLine(MakefileLine &line); 68 | void completePreprocessingDirectiveLine(MakefileLine &line); 69 | bool parseMacro(const QString& line); 70 | bool parsePreprocessingDirective(const QString& line); 71 | QString findIncludeFile(const QString &filePathToInclude); 72 | bool isPreprocessingDirective(const QString& line, QString& directive, QString& value); 73 | void skipUntilNextMatchingConditional(); 74 | void error(const QString& msg); 75 | void enterConditional(bool followElseBranch); 76 | void exitConditional(); 77 | int conditionalDepth() { return m_conditionalStack.count(); } 78 | 79 | private: 80 | struct TextFile 81 | { 82 | MakefileLineReader* reader; 83 | QString fileDirectory; 84 | 85 | TextFile() 86 | : reader(0) 87 | {} 88 | 89 | TextFile(const TextFile& rhs) 90 | : reader(rhs.reader), fileDirectory(rhs.fileDirectory) 91 | {} 92 | 93 | TextFile& operator=(const TextFile& rhs) 94 | { 95 | reader = rhs.reader; 96 | fileDirectory = rhs.fileDirectory; 97 | return *this; 98 | } 99 | }; 100 | 101 | QStack m_fileStack; 102 | MacroTable* m_macroTable; 103 | QRegExp m_rexPreprocessingDirective; 104 | QStack m_conditionalStack; 105 | PPExprParser* m_expressionParser; 106 | QStringList m_linesPutBack; 107 | bool m_bInlineFileMode; 108 | }; 109 | 110 | } //namespace NMakeFile 111 | 112 | #endif // PREPROCESSOR_H 113 | -------------------------------------------------------------------------------- /src/jomlib/processenvironment.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of jom. 7 | ** 8 | ** Commercial License Usage 9 | ** Licensees holding valid commercial Qt licenses may use this file in 10 | ** accordance with the commercial license agreement provided with the 11 | ** Software or, alternatively, in accordance with the terms contained in 12 | ** a written agreement between you and The Qt Company. For licensing terms 13 | ** and conditions see https://www.qt.io/terms-conditions. For further 14 | ** information use the contact form at https://www.qt.io/contact-us. 15 | ** 16 | ** GNU General Public License Usage 17 | ** Alternatively, this file may be used under the terms of the GNU 18 | ** General Public License version 3 as published by the Free Software 19 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 20 | ** included in the packaging of this file. Please review the following 21 | ** information to ensure the GNU General Public License requirements will 22 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. 23 | ** 24 | ****************************************************************************/ 25 | 26 | #ifndef PROCESSENVIRONMENT_H 27 | #define PROCESSENVIRONMENT_H 28 | 29 | #include 30 | #include 31 | 32 | namespace NMakeFile { 33 | 34 | /** 35 | * Key for the ProcessEnvironment class. 36 | */ 37 | class ProcessEnvironmentKey 38 | { 39 | public: 40 | ProcessEnvironmentKey(const QString &key) 41 | : m_key(key) 42 | { 43 | } 44 | 45 | ProcessEnvironmentKey(const QLatin1String &key) 46 | : m_key(key) 47 | { 48 | } 49 | 50 | const QString &toQString() const 51 | { 52 | return m_key; 53 | } 54 | 55 | int compare(const ProcessEnvironmentKey &other) const 56 | { 57 | return m_key.compare(other.m_key, Qt::CaseInsensitive); 58 | } 59 | 60 | private: 61 | QString m_key; 62 | }; 63 | 64 | inline bool operator < (const ProcessEnvironmentKey &lhs, const ProcessEnvironmentKey &rhs) 65 | { 66 | return lhs.compare(rhs) < 0; 67 | } 68 | 69 | typedef QMap ProcessEnvironment; 70 | 71 | } // namespace NMakeFile 72 | 73 | #endif // PROCESSENVIRONMENT_H 74 | -------------------------------------------------------------------------------- /src/jomlib/stable.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of jom. 7 | ** 8 | ** Commercial License Usage 9 | ** Licensees holding valid commercial Qt licenses may use this file in 10 | ** accordance with the commercial license agreement provided with the 11 | ** Software or, alternatively, in accordance with the terms contained in 12 | ** a written agreement between you and The Qt Company. For licensing terms 13 | ** and conditions see https://www.qt.io/terms-conditions. For further 14 | ** information use the contact form at https://www.qt.io/contact-us. 15 | ** 16 | ** GNU General Public License Usage 17 | ** Alternatively, this file may be used under the terms of the GNU 18 | ** General Public License version 3 as published by the Free Software 19 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 20 | ** included in the packaging of this file. Please review the following 21 | ** information to ensure the GNU General Public License requirements will 22 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. 23 | ** 24 | ****************************************************************************/ 25 | 26 | #define _CRT_RAND_S 27 | #include 28 | 29 | #include 30 | -------------------------------------------------------------------------------- /src/jomlib/targetexecutor.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of jom. 7 | ** 8 | ** Commercial License Usage 9 | ** Licensees holding valid commercial Qt licenses may use this file in 10 | ** accordance with the commercial license agreement provided with the 11 | ** Software or, alternatively, in accordance with the terms contained in 12 | ** a written agreement between you and The Qt Company. For licensing terms 13 | ** and conditions see https://www.qt.io/terms-conditions. For further 14 | ** information use the contact form at https://www.qt.io/contact-us. 15 | ** 16 | ** GNU General Public License Usage 17 | ** Alternatively, this file may be used under the terms of the GNU 18 | ** General Public License version 3 as published by the Free Software 19 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 20 | ** included in the packaging of this file. Please review the following 21 | ** information to ensure the GNU General Public License requirements will 22 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. 23 | ** 24 | ****************************************************************************/ 25 | 26 | #include "targetexecutor.h" 27 | #include "commandexecutor.h" 28 | #include "dependencygraph.h" 29 | #include "jobclient.h" 30 | #include "options.h" 31 | #include "exception.h" 32 | 33 | #include 34 | #include 35 | #include 36 | 37 | namespace NMakeFile { 38 | 39 | TargetExecutor::TargetExecutor(const ProcessEnvironment &environment) 40 | : m_environment(environment) 41 | , m_jobClient(0) 42 | , m_bAborted(false) 43 | , m_allCommandsSuccessfullyExecuted(true) 44 | { 45 | m_makefile = 0; 46 | m_depgraph = new DependencyGraph(); 47 | 48 | for (int i = 0; i < g_options.maxNumberOfJobs; ++i) { 49 | CommandExecutor* executor = new CommandExecutor(this, environment); 50 | connect(executor, SIGNAL(finished(CommandExecutor*, bool)), 51 | this, SLOT(onChildFinished(CommandExecutor*, bool))); 52 | 53 | foreach (CommandExecutor *other, m_processes) { 54 | connect(executor, SIGNAL(environmentChanged(const ProcessEnvironment &)), 55 | other, SLOT(setEnvironment(const ProcessEnvironment &))); 56 | connect(other, SIGNAL(environmentChanged(const ProcessEnvironment &)), 57 | executor, SLOT(setEnvironment(const ProcessEnvironment &))); 58 | } 59 | m_processes.append(executor); 60 | } 61 | m_availableProcesses = m_processes; 62 | m_availableProcesses.first()->setBufferedOutput(false); 63 | } 64 | 65 | TargetExecutor::~TargetExecutor() 66 | { 67 | delete m_depgraph; 68 | } 69 | 70 | void TargetExecutor::apply(Makefile* mkfile, const QStringList& targets) 71 | { 72 | m_bAborted = false; 73 | m_allCommandsSuccessfullyExecuted = true; 74 | m_makefile = mkfile; 75 | m_jobAcquisitionCount = 0; 76 | m_nextTarget = 0; 77 | 78 | if (!m_jobClient) { 79 | m_jobClient = new JobClient(&m_environment, this); 80 | if (!m_jobClient->start()) { 81 | const QString msg = QLatin1String("Can't connect to job server: %1"); 82 | throw Exception(msg.arg(m_jobClient->errorString())); 83 | } 84 | connect(m_jobClient, &JobClient::acquired, this, &TargetExecutor::buildNextTarget); 85 | } 86 | 87 | DescriptionBlock* descblock; 88 | if (targets.isEmpty()) { 89 | if (mkfile->targets().isEmpty()) { 90 | finishBuild(0); 91 | return; 92 | } 93 | descblock = mkfile->firstTarget(); 94 | } else { 95 | const QString targetName = targets.first(); 96 | descblock = mkfile->target(targetName); 97 | if (!descblock) { 98 | QString msg = QLatin1String("Target %1 does not exist in %2."); 99 | throw Exception(msg.arg(targetName, mkfile->fileName())); 100 | } 101 | for (int i=1; i < targets.count(); ++i) { 102 | m_pendingTargets.append( mkfile->target(targets.at(i)) ); 103 | } 104 | } 105 | 106 | m_depgraph->build(descblock); 107 | if (m_makefile->options()->dumpDependencyGraph) { 108 | if (m_makefile->options()->dumpDependencyGraphDot) 109 | m_depgraph->dotDump(); 110 | else 111 | m_depgraph->dump(); 112 | finishBuild(0); 113 | return; 114 | } 115 | 116 | QMetaObject::invokeMethod(this, "startProcesses", Qt::QueuedConnection); 117 | } 118 | 119 | void TargetExecutor::startProcesses() 120 | { 121 | if (m_bAborted || m_jobClient->isAcquiring() || m_availableProcesses.isEmpty()) 122 | return; 123 | 124 | try { 125 | if (!m_nextTarget) 126 | findNextTarget(); 127 | 128 | if (m_nextTarget) { 129 | if (numberOfRunningProcesses() == 0) { 130 | // Use up the internal job token. 131 | buildNextTarget(); 132 | } else { 133 | // Acquire a job token from the server. Will call buildNextTarget() when done. 134 | m_jobAcquisitionCount++; 135 | m_jobClient->asyncAcquire(); 136 | } 137 | } else { 138 | if (numberOfRunningProcesses() == 0) { 139 | if (m_pendingTargets.isEmpty()) { 140 | finishBuild(0); 141 | } else { 142 | m_depgraph->clear(); 143 | m_makefile->invalidateTimeStamps(); 144 | m_depgraph->build(m_pendingTargets.takeFirst()); 145 | QMetaObject::invokeMethod(this, "startProcesses", Qt::QueuedConnection); 146 | } 147 | } 148 | } 149 | } catch (Exception &e) { 150 | m_bAborted = true; 151 | fprintf(stderr, "Error: %s\n", qPrintable(e.message())); 152 | finishBuild(1); 153 | } 154 | } 155 | 156 | void TargetExecutor::buildNextTarget() 157 | { 158 | Q_ASSERT(m_nextTarget); 159 | 160 | if (m_bAborted) 161 | return; 162 | 163 | try { 164 | CommandExecutor *executor = m_availableProcesses.takeFirst(); 165 | executor->start(m_nextTarget); 166 | m_nextTarget = 0; 167 | QMetaObject::invokeMethod(this, "startProcesses", Qt::QueuedConnection); 168 | } catch (const Exception &e) { 169 | m_bAborted = true; 170 | fprintf(stderr, "Error: %s\n", qPrintable(e.message())); 171 | finishBuild(1); 172 | } 173 | } 174 | 175 | void TargetExecutor::waitForProcesses() 176 | { 177 | foreach (CommandExecutor* process, m_processes) 178 | process->waitForFinished(); 179 | } 180 | 181 | void TargetExecutor::waitForJobClient() 182 | { 183 | if (!m_jobClient->isAcquiring()) 184 | return; 185 | QEventLoop loop; 186 | connect(m_jobClient, &JobClient::acquired, &loop, &QEventLoop::quit); 187 | loop.exec(); 188 | m_jobClient->release(); 189 | } 190 | 191 | void TargetExecutor::finishBuild(int exitCode) 192 | { 193 | if (exitCode == 0 194 | && !m_allCommandsSuccessfullyExecuted 195 | && m_makefile->options()->buildUnrelatedTargetsOnError) 196 | { 197 | // /k specified and some command failed 198 | exitCode = 1; 199 | } 200 | emit finished(exitCode); 201 | } 202 | 203 | void TargetExecutor::findNextTarget() 204 | { 205 | forever { 206 | m_nextTarget = m_depgraph->findAvailableTarget(m_makefile->options()->buildAllTargets); 207 | if (m_nextTarget) { 208 | if (m_nextTarget->m_commands.isEmpty()) { 209 | // Short cut for targets without commands. 210 | m_depgraph->removeLeaf(m_nextTarget); 211 | continue; 212 | } else if (m_makefile->options()->buildUnrelatedTargetsOnError 213 | && m_depgraph->isUnbuildable(m_nextTarget)) { 214 | fprintf(stderr, "jom: Target '%s' cannot be built due to failed dependencies.\n", 215 | qPrintable(m_nextTarget->targetName())); 216 | m_depgraph->removeLeaf(m_nextTarget); 217 | continue; 218 | } 219 | } 220 | return; 221 | } 222 | } 223 | 224 | void TargetExecutor::onChildFinished(CommandExecutor* executor, bool commandFailed) 225 | { 226 | Q_CHECK_PTR(executor->target()); 227 | if (commandFailed) { 228 | m_allCommandsSuccessfullyExecuted = false; 229 | if (m_makefile->options()->buildUnrelatedTargetsOnError) { 230 | // Recursively mark all parents of this node as unbuildable due to unsatisfied 231 | // dependencies. This must happen before removing the node from the build graph. 232 | m_depgraph->markParentsRecursivlyUnbuildable(executor->target()); 233 | fputs("jom: Option /K specified. Continuing.\n", stderr); 234 | } 235 | } 236 | FastFileInfo::clearCacheForFile(executor->target()->targetName()); 237 | m_depgraph->removeLeaf(executor->target()); 238 | if (m_jobAcquisitionCount > 0) { 239 | m_jobClient->release(); 240 | m_jobAcquisitionCount--; 241 | } 242 | m_availableProcesses.append(executor); 243 | if (!executor->isBufferedOutputSet()) { 244 | executor->setBufferedOutput(true); 245 | bool found = false; 246 | foreach (CommandExecutor *cmdex, m_processes) { 247 | if (cmdex->isActive()) { 248 | cmdex->setBufferedOutput(false); 249 | found = true; 250 | } 251 | } 252 | if (!found) 253 | m_availableProcesses.first()->setBufferedOutput(false); 254 | } 255 | 256 | bool abortMakeProcess = commandFailed && !m_makefile->options()->buildUnrelatedTargetsOnError; 257 | if (abortMakeProcess) { 258 | m_bAborted = true; 259 | m_depgraph->clear(); 260 | m_pendingTargets.clear(); 261 | waitForProcesses(); 262 | waitForJobClient(); 263 | finishBuild(2); 264 | } 265 | 266 | QMetaObject::invokeMethod(this, "startProcesses", Qt::QueuedConnection); 267 | } 268 | 269 | int TargetExecutor::numberOfRunningProcesses() const 270 | { 271 | return m_processes.count() - m_availableProcesses.count(); 272 | } 273 | 274 | void TargetExecutor::removeTempFiles() 275 | { 276 | foreach (QObject* child, children()) { 277 | CommandExecutor* cmdex = qobject_cast(child); 278 | if (!cmdex) 279 | continue; 280 | 281 | cmdex->cleanupTempFiles(); 282 | } 283 | } 284 | 285 | } //namespace NMakeFile 286 | -------------------------------------------------------------------------------- /src/jomlib/targetexecutor.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2016 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of jom. 7 | ** 8 | ** Commercial License Usage 9 | ** Licensees holding valid commercial Qt licenses may use this file in 10 | ** accordance with the commercial license agreement provided with the 11 | ** Software or, alternatively, in accordance with the terms contained in 12 | ** a written agreement between you and The Qt Company. For licensing terms 13 | ** and conditions see https://www.qt.io/terms-conditions. For further 14 | ** information use the contact form at https://www.qt.io/contact-us. 15 | ** 16 | ** GNU General Public License Usage 17 | ** Alternatively, this file may be used under the terms of the GNU 18 | ** General Public License version 3 as published by the Free Software 19 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 20 | ** included in the packaging of this file. Please review the following 21 | ** information to ensure the GNU General Public License requirements will 22 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. 23 | ** 24 | ****************************************************************************/ 25 | 26 | #ifndef TARGETEXECUTOR_H 27 | #define TARGETEXECUTOR_H 28 | 29 | #include "makefile.h" 30 | #include 31 | #include 32 | #include 33 | 34 | QT_BEGIN_NAMESPACE 35 | class QFile; 36 | QT_END_NAMESPACE 37 | 38 | namespace NMakeFile { 39 | 40 | class CommandExecutor; 41 | class DependencyGraph; 42 | class JobClient; 43 | 44 | class TargetExecutor : public QObject { 45 | Q_OBJECT 46 | public: 47 | TargetExecutor(const ProcessEnvironment &environment); 48 | ~TargetExecutor(); 49 | 50 | void apply(Makefile* mkfile, const QStringList& targets); 51 | void removeTempFiles(); 52 | 53 | signals: 54 | void finished(int exitCode); 55 | 56 | private slots: 57 | void startProcesses(); 58 | void buildNextTarget(); 59 | void onChildFinished(CommandExecutor*, bool commandFailed); 60 | 61 | private: 62 | int numberOfRunningProcesses() const; 63 | void waitForProcesses(); 64 | void waitForJobClient(); 65 | void finishBuild(int exitCode); 66 | void findNextTarget(); 67 | 68 | private: 69 | ProcessEnvironment m_environment; 70 | Makefile* m_makefile; 71 | DependencyGraph* m_depgraph; 72 | QList m_pendingTargets; 73 | JobClient *m_jobClient; 74 | bool m_bAborted; 75 | int m_jobAcquisitionCount; 76 | QList m_availableProcesses; 77 | QList m_processes; 78 | DescriptionBlock *m_nextTarget; 79 | bool m_allCommandsSuccessfullyExecuted; 80 | }; 81 | 82 | } //namespace NMakeFile 83 | 84 | #endif // TARGETEXECUTOR_H 85 | -------------------------------------------------------------------------------- /src/jomlib/use_jomlib.pri: -------------------------------------------------------------------------------- 1 | isEmpty(PROJECT_BUILD_ROOT):error(PROJECT_BUILD_ROOT must be set) 2 | 3 | win32-g++ { 4 | JOMLIB_PREFIX = lib 5 | JOMLIB_SUFFIX = a 6 | } else { 7 | JOMLIB_PREFIX = 8 | JOMLIB_SUFFIX = lib 9 | } 10 | 11 | build_pass:CONFIG(debug, debug|release) { 12 | JOMLIB = $$PROJECT_BUILD_ROOT/lib/$${JOMLIB_PREFIX}jomlibd.$$JOMLIB_SUFFIX 13 | } 14 | build_pass:CONFIG(release, debug|release) { 15 | JOMLIB = $$PROJECT_BUILD_ROOT/lib/$${JOMLIB_PREFIX}jomlib.$$JOMLIB_SUFFIX 16 | } 17 | 18 | LIBS += $$JOMLIB 19 | POST_TARGETDEPS += $$JOMLIB 20 | unset(JOMLIB) 21 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(Qt5 5.2.0 REQUIRED COMPONENTS Test) 2 | add_executable(jom-test 3 | tests.cpp tests.h) 4 | 5 | target_link_libraries(jom-test PRIVATE jomlib Qt5::Test) 6 | 7 | add_test(NAME jom-test COMMAND jom-test) 8 | 9 | add_custom_command(TARGET jom-test POST_BUILD 10 | COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/makefiles $/makefiles 11 | ) 12 | -------------------------------------------------------------------------------- /tests/makefiles/blackbox/buildUnrelatedTargetsOnError/test.mk: -------------------------------------------------------------------------------- 1 | # test the /k option 2 | # When running "jom /f test.mk /k" the target "dependsOnFailingTarget" must not be built. 3 | 4 | first: workingTarget dependsOnFailingTarget 5 | 6 | failingTarget: 7 | cmd /c exit 7 8 | 9 | dependsOnFailingTarget: failingTarget 10 | @echo We should not see this. 11 | 12 | workingTarget: 13 | @echo Yay! This always works! 14 | -------------------------------------------------------------------------------- /tests/makefiles/blackbox/builtins/cd.mk: -------------------------------------------------------------------------------- 1 | first: 2 | @echo Default target does nothing. Specify another one. 3 | 4 | provide_subdir: 5 | @if not exist subdir md subdir 6 | 7 | # expected result: this is supposed to output the subdir directory 8 | test1: provide_subdir 9 | @cd subdir 10 | @cd 11 | 12 | # expected result: this is supposed to output the subdir directory 13 | test2: provide_subdir 14 | @cd /D subdir 15 | @cd 16 | 17 | # expected result: this is supposed to output the current directory (not subdir) 18 | # Why? Because of the && nmake executes this command with cmd and loses the 19 | # current working directory after cmd completed. 20 | test3: provide_subdir 21 | @cd subdir && cd 22 | @cd 23 | 24 | -------------------------------------------------------------------------------- /tests/makefiles/blackbox/caseInsensitiveDependents/test.mk: -------------------------------------------------------------------------------- 1 | all: someFile.txt 2 | 3 | SOMEFILE.txt: 4 | @echo somefile.txt 5 | 6 | -------------------------------------------------------------------------------- /tests/makefiles/blackbox/environmentVariables/test.mk: -------------------------------------------------------------------------------- 1 | VAR1 = file 2 | all: 3 | @echo VAR1 $(VAR1) 4 | @echo VAR2 $(VAR2) 5 | 6 | -------------------------------------------------------------------------------- /tests/makefiles/blackbox/environmentVariablesCaseInsensitivity/test.mk: -------------------------------------------------------------------------------- 1 | VAR1 = $(VAR1);C:\Zort 2 | first: 3 | echo VAR1 $(VAR1) %%VAR1%% 4 | -------------------------------------------------------------------------------- /tests/makefiles/blackbox/environmentVariablesInCommands/test.mk: -------------------------------------------------------------------------------- 1 | first: 2 | set SOME_VARIABLE=narf 3 | cmd.exe /c echo %%SOME_VARIABLE%% 4 | -------------------------------------------------------------------------------- /tests/makefiles/blackbox/ignoreExitCodes/test.mk: -------------------------------------------------------------------------------- 1 | # test ignore exit codes in make files 2 | 3 | test1: testNonexistentCommand testExitCode1 testExitCode2 testExitCode3 4 | @echo ---SUCCESS--- 5 | 6 | testNonexistentCommand: 7 | -nonexistent_command 2>NUL 8 | 9 | testExitCode1: 10 | -cmd /k exit 1 11 | 12 | testExitCode2: 13 | -1cmd /k exit 1 14 | 15 | testExitCode3: 16 | ------1234cmd /k exit 1234 17 | 18 | # target test2 is supposed to fail 19 | test2: 20 | -6cmd /k exit 7 21 | -------------------------------------------------------------------------------- /tests/makefiles/blackbox/inlineFiles/test.mk: -------------------------------------------------------------------------------- 1 | # test inline files in make files 2 | # 3 | # http://msdn.microsoft.com/en-us/library/1whxt45w.aspx 4 | 5 | all: 6 | @echo Please specify a target. 7 | 8 | tests: test_basic test_fileRemoval test_keepFile test_multipleFiles test_escaping 9 | 10 | init: 11 | @if exist output rmdir /s /q output 12 | @md output 13 | @echo ::post test check script > output\post_check.cmd 14 | 15 | post_check: 16 | @output\post_check.cmd 17 | 18 | test_basic: init 19 | copy << output\test_basic.txt 20 | line 1 21 | !ifndef UNDEFINED_MACRO 22 | line 2 23 | !endif 24 | line 3 25 | << 26 | 27 | test_fileRemoval: init 28 | echo @if exist << exit /b 1 >> output\post_check.cmd 29 | some random text 30 | << 31 | echo @if exist <> output\post_check.cmd 32 | some random text 33 | << 34 | echo @if exist <> output\post_check.cmd 35 | some random text 36 | <> output\post_check.cmd 40 | some random text 41 | <output\test_multipleFiles.txt 45 | one 46 | two 47 | << 48 | three 49 | four 50 | << 51 | 52 | test_escaping: init 53 | copy << output\test_escaping.txt 54 | InRoot$$$$Sections 55 | # This line should be there. 56 | << 57 | -------------------------------------------------------------------------------- /tests/makefiles/blackbox/inlineFiles/test_basic_expected.txt: -------------------------------------------------------------------------------- 1 | line 1 2 | line 2 3 | line 3 4 | -------------------------------------------------------------------------------- /tests/makefiles/blackbox/inlineFiles/test_escaping_expected.txt: -------------------------------------------------------------------------------- 1 | InRoot$$Sections 2 | # This line should be there. 3 | -------------------------------------------------------------------------------- /tests/makefiles/blackbox/inlineFiles/test_multipleFiles_expected.txt: -------------------------------------------------------------------------------- 1 | one 2 | two 3 | three 4 | four 5 | -------------------------------------------------------------------------------- /tests/makefiles/blackbox/macrosOnCommandLine/test.mk: -------------------------------------------------------------------------------- 1 | FooBar = 1 2 | !message FooBar:$(FooBar) 3 | !message FOOBAR:$(FOOBAR) 4 | -------------------------------------------------------------------------------- /tests/makefiles/blackbox/noTargets/test.mk: -------------------------------------------------------------------------------- 1 | !message Hello there! 2 | -------------------------------------------------------------------------------- /tests/makefiles/blackbox/nonexistentdependent/test.mk: -------------------------------------------------------------------------------- 1 | out: yo notexist 2 | @echo we should not see this 3 | 4 | yo: 5 | @echo yo ho ho ho 6 | 7 | -------------------------------------------------------------------------------- /tests/makefiles/blackbox/outofdatecheck/test.mk: -------------------------------------------------------------------------------- 1 | all: one.txt two three four five six 2 | 3 | clean: 4 | @del one.txt two.txt three.txt four.txt six.txt > NUL 2>&1 5 | 6 | one.txt: 7 | @echo $@ 8 | @echo $@ > $@ 9 | # @ping 127.0.0.1 -n 2 -w 1000 > nul 10 | 11 | two: two.txt 12 | @echo $@ 13 | 14 | two-without-commands: two.txt 15 | 16 | two.txt: 17 | @echo $@ 18 | @echo $@ > $@ 19 | # @ping 127.0.0.1 -n 2 -w 1000 > nul 20 | 21 | three: three.txt 22 | @echo $@ 23 | 24 | # Insane nmake behavior: 25 | # three.txt is always built because 'two' has commands. 26 | three.txt: two 27 | @echo $@ 28 | @type two.txt > $@ 29 | # @ping 127.0.0.1 -n 2 -w 1000 > nul 30 | 31 | four: four.txt 32 | @echo $@ 33 | 34 | four.txt: two.txt 35 | @echo $@ 36 | @copy two.txt $@ > NUL 37 | # @ping 127.0.0.1 -n 2 -w 1000 > nul 38 | 39 | five: 40 | @echo $@ 41 | 42 | six: six.txt 43 | @echo $@ 44 | 45 | # Insane nmake behavior: 46 | # six.txt is only built when two.txt has changed because 'two-without-commands' 47 | # is a real pseudotarget. 48 | six.txt: two-without-commands 49 | @echo $@ 50 | @type two.txt > $@ 51 | 52 | -------------------------------------------------------------------------------- /tests/makefiles/blackbox/suffixes/one.a: -------------------------------------------------------------------------------- 1 | foo 2 | -------------------------------------------------------------------------------- /tests/makefiles/blackbox/suffixes/one.b: -------------------------------------------------------------------------------- 1 | foo 2 | -------------------------------------------------------------------------------- /tests/makefiles/blackbox/suffixes/one.c: -------------------------------------------------------------------------------- 1 | foo 2 | -------------------------------------------------------------------------------- /tests/makefiles/blackbox/suffixes/test.mk: -------------------------------------------------------------------------------- 1 | .SUFFIXES: 2 | .SUFFIXES: .a .b .c .d 3 | 4 | .a.x: 5 | @echo a -^> x 6 | .b.x: 7 | @echo b -^> x 8 | .c.x: 9 | @echo c -^> x 10 | 11 | all: one two.x three.x 12 | one: one.x 13 | two.x: one.x 14 | three.x: two.x 15 | 16 | -------------------------------------------------------------------------------- /tests/makefiles/blackbox/suffixes/three.c: -------------------------------------------------------------------------------- 1 | foo 2 | -------------------------------------------------------------------------------- /tests/makefiles/blackbox/suffixes/three.d: -------------------------------------------------------------------------------- 1 | foo 2 | -------------------------------------------------------------------------------- /tests/makefiles/blackbox/suffixes/two.b: -------------------------------------------------------------------------------- 1 | foo 2 | -------------------------------------------------------------------------------- /tests/makefiles/blackbox/suffixes/two.c: -------------------------------------------------------------------------------- 1 | foo 2 | -------------------------------------------------------------------------------- /tests/makefiles/blackbox/unicodeFiles/test_utf16.mk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qt-labs/jom/5b295dbee66504d21b8cc7d05d5e88e87644d586/tests/makefiles/blackbox/unicodeFiles/test_utf16.mk -------------------------------------------------------------------------------- /tests/makefiles/blackbox/unicodeFiles/test_utf8.mk: -------------------------------------------------------------------------------- 1 | # file encoding: UTF8 with signature 2 | 3 | all: 4 | if not exist "куда Я прошу" md "куда Я прошу" 5 | cd "куда Я прошу" 6 | cd .. 7 | -------------------------------------------------------------------------------- /tests/makefiles/circular_include.mk: -------------------------------------------------------------------------------- 1 | !include circular_Include2.mk 2 | CIRCULAR_INCLUDE = TRUE 3 | 4 | -------------------------------------------------------------------------------- /tests/makefiles/circular_include2.mk: -------------------------------------------------------------------------------- 1 | !include circular_Include.mk 2 | CIRCULAR_INCLUDE2 = TRUE 3 | 4 | -------------------------------------------------------------------------------- /tests/makefiles/commandmodifiers.mk: -------------------------------------------------------------------------------- 1 | first: dep1 dep2 2 | @echo silent command 3 | -echo no error checking 4 | -5echo maximum exit code is 5 5 | -15echo maximum exit code is 15 6 | !echo command for each dependent $** 7 | 8 | dep1: 9 | dep2: 10 | -------------------------------------------------------------------------------- /tests/makefiles/comments.mk: -------------------------------------------------------------------------------- 1 | # this is just a comment 2 | !IF 1 # - 1 3 | COMPILER = Ada95 # here's a comment after a macro definition 4 | DEF = ^#define # ignore me 5 | 6 | first: second third # la la la la 7 | echo I'm Winneone 8 | # foooooo 9 | # baaaaar 10 | echo I'm Winnetou 11 | # Wie viele Winnes haben wir in gesamt? 12 | second: 13 | third: 14 | 15 | .cpp.obj: # and another one 16 | echo inference rule applied 17 | 18 | forth: fifth ; echo # this is no comment 19 | echo # this neither 20 | fifth: file^#99.txt # This is a comment! 21 | echo $** 22 | !ENDIF 23 | 24 | .SILENT: # comment after dot directive 25 | 26 | -------------------------------------------------------------------------------- /tests/makefiles/conditionals.mk: -------------------------------------------------------------------------------- 1 | TEST1=false 2 | !if 1 3 | TEST1=true 4 | !elseif 0 5 | TEST1=false 6 | !elseif 0 7 | TEST1=false 8 | !endif 9 | 10 | TEST2=false 11 | !if 0 12 | TEST2=false 13 | !elseif 0 14 | # some comment 15 | # more to say... 16 | NOOP=1 17 | TEST2=false 18 | !elseif 1 19 | NOOP=1 20 | TEST2=true 21 | NOOP=1 22 | !endif 23 | 24 | TEST3=false 25 | !if 0 # comment 26 | TEST3=false 27 | !elseif 1#another comment 28 | TEST3=true 29 | !elseif 1 # comment 30 | TEST3=false 31 | !endif 32 | 33 | TEST4=false 34 | !IFDEF TEST1 35 | TEST4=true 36 | !ELSEIFDEF NOT_DEFINED 37 | TEST4=false 38 | !ELSE 39 | TEST4=false 40 | !ENDIF 41 | 42 | TEST5=false 43 | !IFDEF NOT_DEFINED 44 | TEST5=false 45 | !ELSEIFDEF TEST1 46 | TEST5=true 47 | !ELSE 48 | TEST5=false 49 | !ENDIF 50 | 51 | TEST6=false 52 | !IFDEF NOT_DEFINED 53 | TEST6=false 54 | !ELSEIFDEF NOT_DEFINED 55 | TEST6=false 56 | !ELSE 57 | TEST6=true 58 | !ENDIF 59 | 60 | TEST7=false 61 | !IFDEF NOT_DEFINED 62 | TEST7=false 63 | !ELSEIFNDEF NOT_DEFINED 64 | TEST7=true 65 | !ELSE 66 | TEST7=false 67 | !ENDIF 68 | 69 | TEST8=false 70 | !IFNDEF NOT_DEFINED 71 | TEST8=true 72 | !ELSEIFNDEF NOT_DEFINED 73 | TEST8=false 74 | !ELSE 75 | TEST8=false 76 | !ENDIF 77 | 78 | TEST9=foo \ 79 | bar \ 80 | !IF 1 81 | baz \ 82 | !ELSE 83 | boo \ 84 | hoo 85 | !ENDIF 86 | 87 | TEST10=foo \ 88 | bar \ 89 | !IF 0 90 | baz \ 91 | !ELSE 92 | boo \ 93 | hoo 94 | !ENDIF 95 | 96 | all: 97 | 98 | -------------------------------------------------------------------------------- /tests/makefiles/cycle_in_targets.mk: -------------------------------------------------------------------------------- 1 | first: foo 2 | 3 | foo: bar 4 | 5 | bar: schnusel 6 | 7 | schnusel: foo 8 | 9 | -------------------------------------------------------------------------------- /tests/makefiles/depswithspace.mk: -------------------------------------------------------------------------------- 1 | first: one "dependent two with spaces" three 2 | echo $** 3 | 4 | one: 5 | "dependent two with spaces": 6 | three: 7 | 8 | -------------------------------------------------------------------------------- /tests/makefiles/descriptionblocks.mk: -------------------------------------------------------------------------------- 1 | all : one two three dollarSigns 2 | 3 | DEPENDENT_C=c 4 | 5 | one: a b $(DEPENDENT_C) 6 | @echo one 7 | two:; @echo two 8 | three : "a;b" ;@echo three; @echo end of three 9 | four: ; @echo four=4 10 | 11 | a : 12 | b : 13 | c : 14 | "a;b": 15 | @echo X 16 | 17 | DIRECTORYNAME=. 18 | $(DIRECTORYNAME): 19 | @echo directory $(DIRECTORYNAME) doesn't exist. That's strange. 20 | "$(DIRECTORYNAME)": 21 | @echo directory "$(DIRECTORYNAME)" doesn't exist. That's strange. 22 | 23 | DIRECTORYNAME=.. 24 | $(DIRECTORYNAME): 25 | @echo directory $(DIRECTORYNAME) doesn't exist. That's strange. 26 | "$(DIRECTORYNAME)": 27 | @echo directory "$(DIRECTORYNAME)" doesn't exist. That's strange. 28 | 29 | dollarSigns: 30 | @echo ($$dollar-signs$$) 31 | @echo $$(dollar-signs)$$ 32 | 33 | TARGETNAME=XTargetName 34 | $(TARGETNAME:X=Substituted): dep1 dep2 dep3 35 | @echo target name with macro substitution 36 | -------------------------------------------------------------------------------- /tests/makefiles/dotdirectives.mk: -------------------------------------------------------------------------------- 1 | all: silence ignorance preciousness suffixes 2 | 3 | silence: silence_one silence_two silence_three 4 | silence_one: 5 | echo 1 6 | $(NOT_DEFINED).SILENT: 7 | silence_two: 8 | echo 2 9 | $(NOT_DEFINED)!CMDSWITCHES -S 10 | silence_three: 11 | echo 3 12 | 13 | ignorance: ignorance_one ignorance_two ignorance_three 14 | ignorance_one: 15 | echo 1 16 | $(NOT_DEFINED).IGNORE : 17 | ignorance_two: 18 | echo 2 19 | cmd /c exit /b 1 20 | $(NOT_DEFINED)! CMDSWITCHES -i 21 | ignorance_three: 22 | echo 3 23 | 24 | preciousness: preciousness_one preciousness_two preciousness_three 25 | $(NOT_DEFINED).PRECIOUS : preciousness_one preciousness_two preciousness_three 26 | preciousness_one: 27 | preciousness_two: 28 | preciousness_three: 29 | 30 | $(NOT_DEFINED).SUFFIXES: .exe .obj 31 | suffixes: 32 | -------------------------------------------------------------------------------- /tests/makefiles/file#99.txt: -------------------------------------------------------------------------------- 1 | What a nice file name. 2 | -------------------------------------------------------------------------------- /tests/makefiles/fileNameMacrosInDependents.mk: -------------------------------------------------------------------------------- 1 | all: 2 | DEPS=$@ $$@ $* $(@D) $$(@D) $(*D) $(@B) $$(@B) $(*B) $(@F) $$(@F) $(*F) $(@R) $$(@R) $(*R) 3 | foo: $(DEPS) 4 | foo.obj: $(DEPS) 5 | C:\MyProject\tmp\foo.obj: $(DEPS) 6 | "C:\My Project\tmp\foo.obj": $(DEPS) 7 | -------------------------------------------------------------------------------- /tests/makefiles/filenamemacros.mk: -------------------------------------------------------------------------------- 1 | all: Footb$$@ LolCatExtractorManager.tar.gz manyDependents \ 2 | manyDependentsSingleExecution manyDependentsSubstitutedNames \ 3 | manyDependentsInlineFile \ 4 | gen_init generated.txt gen_cleanup macros.mk $(MAKEDIR)\infrules.mk \ 5 | root_dir\substitutionWithColon nonexistenttarget 6 | 7 | # $** and $? are undefined here and should be evaluated to empty strings. 8 | Football: 9 | @echo $$@ 10 | @echo $@ $@ 11 | @echo $$** $** 12 | @echo $$? $? 13 | @echo $$(**) $(**) 14 | @echo $$(?) $(?) 15 | 16 | LolCatExtractorManager.tar.gz: 17 | @echo $* 18 | 19 | dependencyWithSpaces: foo "broken arrow" bar 20 | @echo $$** $** 21 | 22 | manyDependents: "Mr. Garrison" Timmy Jimmy Kenny Eric Kyle Stan 23 | @echo $** 24 | @echo $? 25 | 26 | manyDependentsSingleExecution: "Mr. Garrison" Timmy Jimmy Kenny Eric Kyle Stan 27 | !@echo $** 28 | !@echo $? 29 | 30 | manyDependentsSubstitutedNames: Tilly Jilly 31 | @echo $$(**) $(**) 32 | @echo $$(?) $(?) 33 | @echo $$(**:ll=mm) $(**:ll=mm) 34 | @echo $$(?:ll=mm) $(?:ll=mm) 35 | 36 | manyDependentsInlineFile: Timmy Jimmy Kenny Eric Kyle Stan 37 | @type << 38 | $$@ $@ 39 | $$** $** 40 | << 41 | 42 | manyDependentsWithModifiers: subdir\Timmy.txt subdir\subsubdir\Jimmy.txt Kenny.txt Eric.txt Kyle.txt Stan.txt 43 | @echo $$(**D) $(**D) 44 | @echo $$(**B) $(**B) 45 | @echo $$(**F) $(**F) 46 | @echo $$(**R) $(**R) 47 | @echo $$(?D) $(?D) 48 | @echo $$(?B) $(?B) 49 | @echo $$(?F) $(?F) 50 | @echo $$(?R) $(?R) 51 | 52 | Timmy: 53 | Jimmy: 54 | Kenny: 55 | Eric: 56 | Kyle: 57 | Stan: 58 | Tilly: 59 | Jilly: 60 | 61 | subdir\Timmy.txt: 62 | subdir\subsubdir\Jimmy.txt: 63 | Kenny.txt: 64 | Eric.txt: 65 | Kyle.txt: 66 | Stan.txt: 67 | Tilly.txt: 68 | Jilly.txt: 69 | 70 | gen_init: 71 | @echo x > gen1.txt 72 | @echo x > generated.txt 73 | @echo x > gen2.txt 74 | @echo x > gen3.txt 75 | 76 | generated.txt: gen1.txt gen2.txt gen3.txt 77 | @echo $? 78 | 79 | gen_cleanup: 80 | @del generated.txt gen?.txt 81 | 82 | macros.mk: 83 | @echo $$(@D) $(@D) 84 | @echo $$(@B) $(@B) 85 | @echo $$(@F) $(@F) 86 | @echo $$(@R) $(@R) 87 | 88 | $(MAKEDIR)\infrules.mk: force 89 | @echo $$(@D) $(@D) 90 | @echo $$(@B) $(@B) 91 | @echo $$(@F) $(@F) 92 | @echo $$(@R) $(@R) 93 | 94 | force: 95 | 96 | root_dir\substitutionWithColon: 97 | @echo $(@:root_dir=C:\somewhere) 98 | @echo $(@R:root_dir=C:\somewhere) 99 | 100 | nonexistenttarget: filenamemacros.mk 101 | @echo $$? $? 102 | -------------------------------------------------------------------------------- /tests/makefiles/foo1.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qt-labs/jom/5b295dbee66504d21b8cc7d05d5e88e87644d586/tests/makefiles/foo1.cpp -------------------------------------------------------------------------------- /tests/makefiles/foo3.cpp: -------------------------------------------------------------------------------- 1 | I am temporary. Delete me. 2 | -------------------------------------------------------------------------------- /tests/makefiles/foo4.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qt-labs/jom/5b295dbee66504d21b8cc7d05d5e88e87644d586/tests/makefiles/foo4.cpp -------------------------------------------------------------------------------- /tests/makefiles/include1.mk: -------------------------------------------------------------------------------- 1 | # Make sure that this file does NOT have a newline at the end! 2 | !message "include 1" 3 | INCLUDE1=TRUE -------------------------------------------------------------------------------- /tests/makefiles/include2.mk: -------------------------------------------------------------------------------- 1 | !message "include 2" 2 | INCLUDE2=TRUE 3 | 4 | -------------------------------------------------------------------------------- /tests/makefiles/include5.mk: -------------------------------------------------------------------------------- 1 | !message "include 5" 2 | INCLUDE5 = TRUE 3 | 4 | -------------------------------------------------------------------------------- /tests/makefiles/include_test.mk: -------------------------------------------------------------------------------- 1 | !include $(NOT_DEFINED)include1.mk 2 | include include2.mk # old style include directive 3 | 4 | INCLUDE = $(INCLUDE);subdir 5 | ! include 6 | !include "subdir\include4.mk" 7 | !include <"include9.mk"> 8 | 9 | includeFoo: # this is not an include directive! 10 | 11 | -------------------------------------------------------------------------------- /tests/makefiles/infrules.mk: -------------------------------------------------------------------------------- 1 | DEPNAME=($<) 2 | 3 | $(NOT_DEFINED).cpp$(NOT_DEFINED).obj: 4 | @echo .cpp.obj $(DEPNAME) 5 | 6 | {subdir}.cpp.obj: 7 | @echo {subdir}.cpp.obj ($<) 8 | 9 | all: init\ 10 | foo1.obj foo2.obj foo3.obj foo4.obj\ 11 | foo5.obj "foo6.obj" foo.bar.obj\ 12 | cleanup 13 | 14 | init: 15 | @echo I am temporary. Delete me. > foo6.cpp 16 | @echo I am temporary. Delete me. > foo5.cpp 17 | @echo I am temporary. Delete me. > subdir\foo5.cpp 18 | @echo I am temporary. Delete me. > foo.bar.cpp 19 | 20 | cleanup: 21 | @del foo6.cpp 22 | @del foo5.cpp 23 | @del subdir\foo5.cpp 24 | @del foo.bar.cpp 25 | 26 | foo4.obj: foo4.cpp 27 | 28 | foo5.obj: foo5.cpp 29 | 30 | "foo6.obj": foo6.cpp 31 | 32 | foo.bar.obj: foo.bar.cpp 33 | 34 | -------------------------------------------------------------------------------- /tests/makefiles/macrotest.mk: -------------------------------------------------------------------------------- 1 | VERY_LONG_Macro_Name_With_mucho_mucho_characters_and_some_number_too_1234458789765421200218427824996512548989654486630110059699471421 = AHA 2 | LOREM = ipsum 3 | SEIN = ist 4 | DES = kleinen 5 | WUPPIWUPPI = wallewalle 6 | ThisIsNotDefined=well, at the moment is actually *is* defined. 7 | viel$(LOREM)_$(SEIN)$(WUPPIWUPPI)_ = Icke wa dsch und er denn uurrrgh... 8 | # what about a comment inbetween? good idea! :-) 9 | NoContent= # this is a comment, dude 10 | LateDefinition = _$(Literal2)_ 11 | Literal1 = ^# who does that anyway? ^# # but here ends the line, sucker! 12 | Literal2 = thi$$ i$$ pricele$$$$ 13 | Literal3 = schnupsi^ 14 | wupsi^ 15 | dupsi 16 | Literal4 = backslash at the end^\ 17 | !UNDEF ThisIsNotDefined 18 | Literal5 = backslash at the end\# 19 | Literal6 = backslash at the end\ # errm i don't have anything to say... 20 | Literal7 = double backslash at the end\\ 21 | SplitOverLines= \ 22 | one \ 23 | two\ 24 | three 25 | Incremental = one 26 | Incremental = $(Incremental) two 27 | 28 | B=B 29 | y=y 30 | $BANANA=$yellow 31 | 32 | 1=x 33 | XXX=$1$(1)$1 34 | 35 | MACROSUBSTITUTION=is not working 36 | MKSPECDIR=root_dir\mkspecs 37 | 38 | _STARTING_WITH_AN_UNDERSCORE=underscores are beautiful 39 | __STARTING_WITH_TWO_UNDERSCORES=underscores are beautiful 40 | -------------------------------------------------------------------------------- /tests/makefiles/subdir/foo1.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qt-labs/jom/5b295dbee66504d21b8cc7d05d5e88e87644d586/tests/makefiles/subdir/foo1.cpp -------------------------------------------------------------------------------- /tests/makefiles/subdir/foo2.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qt-labs/jom/5b295dbee66504d21b8cc7d05d5e88e87644d586/tests/makefiles/subdir/foo2.cpp -------------------------------------------------------------------------------- /tests/makefiles/subdir/foo4.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qt-labs/jom/5b295dbee66504d21b8cc7d05d5e88e87644d586/tests/makefiles/subdir/foo4.cpp -------------------------------------------------------------------------------- /tests/makefiles/subdir/include3.mk: -------------------------------------------------------------------------------- 1 | !message "include 3" 2 | INCLUDE3 = TRUE 3 | 4 | -------------------------------------------------------------------------------- /tests/makefiles/subdir/include4.mk: -------------------------------------------------------------------------------- 1 | !message "include 4" 2 | INCLUDE4 = TRUE 3 | !include include5.mk 4 | !include subdir\subsub\include6.mk 5 | -------------------------------------------------------------------------------- /tests/makefiles/subdir/include7.mk: -------------------------------------------------------------------------------- 1 | !message "include 7" 2 | INCLUDE7 = TRUE 3 | 4 | -------------------------------------------------------------------------------- /tests/makefiles/subdir/include8.mk: -------------------------------------------------------------------------------- 1 | !message "include 8" 2 | INCLUDE8 = TRUE 3 | 4 | -------------------------------------------------------------------------------- /tests/makefiles/subdir/include9.mk: -------------------------------------------------------------------------------- 1 | !message "include 9" 2 | INCLUDE9 = TRUE 3 | 4 | -------------------------------------------------------------------------------- /tests/makefiles/subdir/subsub/include6.mk: -------------------------------------------------------------------------------- 1 | !message "include 6" 2 | INCLUDE6=TRUE 3 | !include include7.mk 4 | !include "include8.mk" 5 | -------------------------------------------------------------------------------- /tests/makefiles/targetmultidef.mk: -------------------------------------------------------------------------------- 1 | all: foo bar 2 | 3 | foo: foo1.cpp 4 | foo: foo3.cpp 5 | foo: foo4.cpp 6 | echo $** 7 | 8 | bar:: foo1.cpp 9 | echo one 10 | bar:: foo3.cpp 11 | echo two 12 | bar:: foo4.cpp 13 | echo three 14 | 15 | -------------------------------------------------------------------------------- /tests/makefiles/wildcardsInDependencies.mk: -------------------------------------------------------------------------------- 1 | all: *.txt foo?.cpp 2 | more: subdir\*.cpp 3 | -------------------------------------------------------------------------------- /tests/makefiles/windowspaths.mk: -------------------------------------------------------------------------------- 1 | C:\foo.txt: 2 | @echo $@ 3 | 4 | C:/bar.txt: 5 | @echo $@ 6 | 7 | "C:\three.txt":: 8 | @echo $@ 9 | 10 | C:\three.txt:: 11 | @echo ... 12 | 13 | S :: 14 | @echo $@ 15 | 16 | "S" :: 17 | @echo ... 18 | -------------------------------------------------------------------------------- /tests/tests.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2015 The Qt Company Ltd. 4 | ** Contact: http://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of the jom project on Trolltech Labs. 7 | ** 8 | ** This file may be used under the terms of the GNU General Public 9 | ** License version 2.0 or 3.0 as published by the Free Software Foundation 10 | ** and appearing in the file LICENSE.GPL included in the packaging of 11 | ** this file. Please review the following information to ensure GNU 12 | ** General Public Licensing requirements will be met: 13 | ** http://www.fsf.org/licensing/licenses/info/GPLv2.html and 14 | ** http://www.gnu.org/copyleft/gpl.html. 15 | ** 16 | ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE 17 | ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 18 | ** 19 | ****************************************************************************/ 20 | 21 | #ifndef TESTS_H 22 | #define TESTS_H 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | namespace NMakeFile 29 | { 30 | class Preprocessor; 31 | class MakefileFactory; 32 | } 33 | 34 | class Tests : public QObject 35 | { 36 | Q_OBJECT 37 | private slots: 38 | void initTestCase(); 39 | void cleanupTestCase(); 40 | 41 | // preprocessor tests 42 | void includeFiles(); 43 | void includeCycle(); 44 | void macros(); 45 | void invalidMacros_data(); 46 | void invalidMacros(); 47 | void preprocessorExpressions_data(); 48 | void preprocessorExpressions(); 49 | void preprocessorDivideByZero(); 50 | void preprocessorInvalidExpressions_data(); 51 | void preprocessorInvalidExpressions(); 52 | void conditionals(); 53 | void dotDirectives(); 54 | 55 | // parser tests 56 | void descriptionBlocks(); 57 | void inferenceRules_data(); 58 | void inferenceRules(); 59 | void cycleInTargets(); 60 | void dependentsWithSpace(); 61 | void multipleTargets(); 62 | void commandModifiers(); 63 | void comments(); 64 | void fileNameMacros(); 65 | void fileNameMacrosInDependents(); 66 | void wildcardsInDependencies(); 67 | void windowsPathsInTargetName(); 68 | 69 | // black-box tests 70 | void buildUnrelatedTargetsOnError(); 71 | void caseInsensitiveDependents(); 72 | void environmentVariables_data(); 73 | void environmentVariables(); 74 | void environmentVariablesCaseInsensitivity(); 75 | void environmentVariablesInCommands(); 76 | void ignoreExitCodes(); 77 | void inlineFiles(); 78 | void unicodeFiles_data(); 79 | void unicodeFiles(); 80 | void builtin_cd_data(); 81 | void builtin_cd(); 82 | void suffixes(); 83 | void macrosOnCommandLine_data(); 84 | void macrosOnCommandLine(); 85 | void nonexistentDependent(); 86 | void noTargets(); 87 | void outOfDateCheck(); 88 | 89 | private: 90 | bool openMakefile(const QString& fileName); 91 | bool runJom(const QStringList &args, const QString &workingDirectory = QString(), 92 | QProcess::ProcessChannelMode channelMode = QProcess::MergedChannels); 93 | bool fileContentsEqual(const QString& fileName1, const QString& fileName2); 94 | QStringList readJomStdOutput(); 95 | void touchFile(const QString &fileName); 96 | static QList splitOutput(const QByteArray &output); 97 | 98 | private: 99 | QString m_oldCurrentPath; 100 | NMakeFile::Preprocessor* m_preprocessor; 101 | NMakeFile::MakefileFactory* m_makefileFactory; 102 | QProcess *m_jomProcess; 103 | }; 104 | 105 | #endif // TESTS_H 106 | -------------------------------------------------------------------------------- /tests/tests.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | QT = core testlib 3 | INCLUDEPATH += ../src/jomlib 4 | 5 | CONFIG(debug, debug|release) { 6 | TARGET = testsd 7 | } else { 8 | TARGET = tests 9 | } 10 | 11 | PROJECT_BUILD_ROOT=$$OUT_PWD/.. 12 | include(../src/jomlib/use_jomlib.pri) 13 | 14 | contains(QMAKE_CXXFLAGS_RELEASE, -MT) { 15 | QMAKE_LFLAGS_RELEASE += /NODEFAULTLIB:msvcrt 16 | } 17 | 18 | HEADERS += tests.h 19 | SOURCES += tests.cpp 20 | 21 | OTHER_FILES += $$files(makefiles/*, true) 22 | --------------------------------------------------------------------------------