Cherry pick changes from wip-scritchui which should be mainline.
[SquirrelJME.git] / nanocoat / cmake / utils.cmake
blobcf29c7a7fd0b83e8d319555a496ca08f9205855f
1 # ---------------------------------------------------------------------------
2 # SquirrelJME
3 #     Copyright (C) Stephanie Gawroriski <xer@multiphasicapps.net>
4 # ---------------------------------------------------------------------------
5 # SquirrelJME is under the Mozilla Public License Version 2.0.
6 # See license.mkd for licensing and copyright information.
7 # ---------------------------------------------------------------------------
8 # DESCRIPTION: Defines the base project and the versioning info
10 # Echo commands accordingly
11 set(CMAKE_EXECUTE_PROCESS_COMMAND_ECHO STDERR)
13 # The directory where the utilities should exist
14 get_filename_component(SQUIRRELJME_UTIL_SOURCE_DIR
15         "${CMAKE_SOURCE_DIR}/cmake/utils" ABSOLUTE)
16 get_filename_component(SQUIRRELJME_UTIL_DIR
17         "${CMAKE_BINARY_DIR}/utils" ABSOLUTE)
19 # Add macro to determine the path of a utility
20 macro(squirreljme_util var what)
21         set(${var}
22                 "${SQUIRRELJME_UTIL_DIR}/${what}${SQUIRRELJME_HOST_EXE_SUFFIX}")
23 endmacro()
25 # Only run this if the directory does not exist, because there might be a
26 # cache from a previous run?
27 if(NOT EXISTS "${SQUIRRELJME_UTIL_DIR}" OR
28         NOT EXISTS "${SQUIRRELJME_UTIL_DIR}/CMakeCache.txt")
29         # Make sure the directory exists
30         file(MAKE_DIRECTORY "${SQUIRRELJME_UTIL_DIR}")
32         # Emscripten breaks here, so do not use it with nested CMake
33         # Also nested CMake breaks here as well
34         if(EMSCRIPTEN OR SQUIRRELJME_CROSS_BUILD)
35                 set(cmakeUtilConfigResult 1)
36         else()
37                 # Note
38                 message(STATUS "Bootstrapping utils into "
39                         "${SQUIRRELJME_UTIL_DIR}...")
40                 message(STATUS "Current generator is ${CMAKE_GENERATOR}...")
42                 # Run nested CMake to build the utilities
43                 if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.13")
44                         # CMake 3.13 added the -S and -B switches
45                         execute_process(
46                                 COMMAND "${CMAKE_COMMAND}"
47                                         "-E" "env"
48                                         "--unset=CMAKE_TOOLCHAIN_FILE"
49                                         "--unset=CMAKE_SOURCE_ROOT"
50                                         "--unset=CMAKE_FRAMEWORK_PATH"
51                                         "--unset=CMAKE_INCLUDE_PATH"
52                                         "--unset=CMAKE_LIBRARY_PATH"
53                                         "--unset=CMAKE_PROGRAM_PATH"
54                                         "--unset=CMAKE_BUILD_TYPE"
55                                         "--unset=CMAKE_GENERATOR"
56                                         "--unset=CMAKE_GENERATOR_INSTANCE"
57                                         "--unset=CMAKE_GENERATOR_PLATFORM"
58                                         "--unset=CMAKE_GENERATOR_TOOLSET"
59                                         "--unset=CMAKE_C_COMPILER_LAUNCHER"
60                                         "--unset=CMAKE_C_LINKER_LAUNCHER"
61                                         "--unset=LDFLAGS"
62                                         "${CMAKE_COMMAND}"
63                                         "-DCMAKE_BUILD_TYPE=Debug"
64                                         "-G" "${CMAKE_GENERATOR}"
65                                         "-S" "${SQUIRRELJME_UTIL_SOURCE_DIR}"
66                                         "-B" "${SQUIRRELJME_UTIL_DIR}"
67                                 RESULT_VARIABLE cmakeUtilBuildResult)
68                 else()
69                         # Need to initialize the project the old way, by just being in
70                         # a different working directory and referring to the source
71                         execute_process(
72                                 COMMAND "${CMAKE_COMMAND}"
73                                         "-E" "env"
74                                         "--unset=CMAKE_TOOLCHAIN_FILE"
75                                         "--unset=CMAKE_SOURCE_ROOT"
76                                         "--unset=CMAKE_FRAMEWORK_PATH"
77                                         "--unset=CMAKE_INCLUDE_PATH"
78                                         "--unset=CMAKE_LIBRARY_PATH"
79                                         "--unset=CMAKE_PROGRAM_PATH"
80                                         "--unset=CMAKE_BUILD_TYPE"
81                                         "--unset=CMAKE_GENERATOR"
82                                         "--unset=CMAKE_GENERATOR_INSTANCE"
83                                         "--unset=CMAKE_GENERATOR_PLATFORM"
84                                         "--unset=CMAKE_GENERATOR_TOOLSET"
85                                         "--unset=CMAKE_C_COMPILER_LAUNCHER"
86                                         "--unset=CMAKE_C_LINKER_LAUNCHER"
87                                         "--unset=LDFLAGS"
88                                         "${CMAKE_COMMAND}"
89                                         "-DCMAKE_BUILD_TYPE=Debug"
90                                         "-G" "${CMAKE_GENERATOR}"
91                                         "${SQUIRRELJME_UTIL_SOURCE_DIR}"
92                                 WORKING_DIRECTORY "${SQUIRRELJME_UTIL_DIR}"
93                                 RESULT_VARIABLE cmakeUtilConfigResult)
94                 endif()
95         endif()
97         # Did this fail?
98         if(cmakeUtilConfigResult)
99                 message(WARNING "Cannot configure utils: "
100                         "${cmakeUtilConfigResult}...")
101         else()
102                 # Determine executable suffix
103                 if(EXISTS "${SQUIRRELJME_UTIL_DIR}/suffix")
104                         file(STRINGS "${SQUIRRELJME_UTIL_DIR}/suffix"
105                                 SQUIRRELJME_HOST_EXE_SUFFIX)
106                         message(DEBUG "Host executable suffix is "
107                                 "'${SQUIRRELJME_HOST_EXE_SUFFIX}'.")
108                 endif()
109         endif()
110 else()
111         message(STATUS
112                 "No need to configure utilities, already there...")
113         set(cmakeUtilConfigResult 0)
114 endif()
116 if (NOT cmakeUtilConfigResult AND
117         NOT EMSCRIPTEN)
118         # Build the utilities, just in case it is out of date
119         message(STATUS "Building utilities, if out of date...")
120         execute_process(
121                 COMMAND "${CMAKE_COMMAND}"
122                         "--build" "${SQUIRRELJME_UTIL_DIR}"
123                 RESULT_VARIABLE cmakeUtilBuildResult
124                 WORKING_DIRECTORY "${SQUIRRELJME_UTIL_DIR}")
126         # Make sure the executable actually runs since it might have built
127         if(NOT cmakeUtilBuildResult)
128                 # Determine path where simple exists
129                 squirreljme_util(cmakeSimpleExe simple)
131                 # Execute it and check if it works
132                 execute_process(COMMAND "${cmakeSimpleExe}"
133                         RESULT_VARIABLE cmakeUtilBuildResult
134                         WORKING_DIRECTORY "${SQUIRRELJME_UTIL_DIR}")
136                 if(cmakeUtilBuildResult)
137                         message(WARNING "Failed to run simple test utility.")
138                 else()
139                         message(STATUS "Simple test ran okay.")
140                 endif()
141         endif()
142 else()
143         set(cmakeUtilBuildResult 1)
144 endif()
146 # Did this fail?
147 if(cmakeUtilBuildResult)
148         # Ignore for now
149         message(WARNING "Cannot build and run utils (CMake): "
150                 "${cmakeUtilBuildResult}...")
152         # Try to find a compiler
153         find_program(HOST_CC "cc")
154         if(NOT HOST_CC)
155                 find_program(HOST_CC "gcc")
156         endif()
158         # Fallback to regular make, maybe it will work
159         execute_process(
160                 COMMAND "${CMAKE_COMMAND}"
161                         "-E" "env"
162                         "--unset=CC"
163                         "--unset=CFLAGS"
164                         "--unset=CXX"
165                         "--unset=CPP"
166                         "--unset=LD"
167                         "--unset=LDFLAGS"
168                         "make" "all"
169                         "OUTPUT_DIR=${SQUIRRELJME_UTIL_DIR}"
170                         "HOST_EXE_SUFFIX=${SQUIRRELJME_HOST_EXE_SUFFIX}"
171                 RESULT_VARIABLE makeUtilBuildResult
172                 WORKING_DIRECTORY "${SQUIRRELJME_UTIL_SOURCE_DIR}")
174         # This failed too...
175         if(makeUtilBuildResult)
176                 message(FATAL_ERROR
177                         "Cannot build utils (Make): "
178                                 "${makeUtilBuildResult}...")
179         endif()
180 endif()
182 # If there is no suffix and we are on Windows, assume .exe
183 if(NOT DEFINED SQUIRRELJME_HOST_EXE_SUFFIX AND
184         CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
185         set(SQUIRRELJME_HOST_EXE_SUFFIX ".exe")
186 endif()
188 # Checks if a given file is out of date according to a checksum
189 function(squirreljme_check_file_checksum upToDateVar
190         inputFile outputPath)
191         # Get hash of input file
192         file(SHA1 "${inputFile}" cacheHash)
194         # Get last checksum, if any
195         if(EXISTS "${outputPath}.checksum")
196                 file(STRINGS "${outputPath}.checksum"
197                         cacheHashLast)
198         else()
199                 set(cacheHashLast "")
200         endif()
202         # Is the checksum out of date?
203         if(NOT EXISTS "${outputPath}.checksum" OR
204                 NOT EXISTS "${outputPath}" OR
205                 NOT "${cacheHash}" STREQUAL "${cacheHashLast}")
206                 set(${upToDateVar} 0 PARENT_SCOPE)
207         else()
208                 set(${upToDateVar} 1 PARENT_SCOPE)
209         endif()
210 endfunction()
212 # Writes the checksum of the input file to the output
213 function(squirreljme_write_file_checksum inputFile
214         outputPath)
215         # Get hash of input file
216         file(SHA1 "${inputFile}" cacheHash)
218         # Store checksum
219         file(WRITE "${outputPath}.checksum"
220                 "${cacheHash}")
221 endfunction()
223 # Decodes the given file
224 function(squirreljme_decode_file how
225         inputPath outputPath)
226         # Should be HEX or BASE64
227         if(NOT how STREQUAL "HEX" AND
228                 NOT how STREQUAL "BASE64")
229                 message(FATAL_ERROR "squirreljme_decode_file() takes "
230                         "either HEX or BASE64")
231         endif()
233         # Where is the decoder?
234         squirreljme_util(decodeExePath decode)
236         # Run the command
237         execute_process(COMMAND "${decodeExePath}" "${how}"
238                 INPUT_FILE "${inputPath}"
239                 OUTPUT_FILE "${outputPath}"
240                 RESULT_VARIABLE conversionExitCode
241                 TIMEOUT 16)
243         # Failed
244         if(conversionExitCode)
245                 message(FATAL_ERROR
246                         "Conversion failed: ${conversionExitCode}.")
247         endif()
248 endfunction()
250 # Decodes a directory of encoded files
251 function(squirreljme_decode_dir inputDir outputDir)
252         # Decode all files accordingly
253         file(GLOB inFiles
254                 "${inputDir}/*.__hex"
255                 "${inputDir}/*.__mime")
256         foreach(inFile ${inFiles})
257                 # Determine the base name of the file
258                 get_filename_component(baseName
259                         "${inFile}" NAME)
261                 # Is this Hex or MIME?
262                 string(FIND "${baseName}" ".__hex"
263                         isHexFile)
265                 # Remove extension from it
266                 string(REPLACE ".__hex" ""
267                         baseName
268                         "${baseName}")
269                 string(REPLACE ".__mime" ""
270                         baseName
271                         "${baseName}")
273                 # Make sure the target directory exists first
274                 file(MAKE_DIRECTORY "${outputDir}")
276                 # Determine input and output
277                 get_filename_component(inFileAbs
278                         "${inFile}" ABSOLUTE)
279                 get_filename_component(outFileAbsPath
280                         "${outputDir}/${baseName}" ABSOLUTE)
282                 # Check if file is up to date
283                 squirreljme_check_file_checksum(upToDate
284                         "${inFileAbs}" "${outFileAbsPath}")
286                 # Does decoding need to be rerun?
287                 if(NOT upToDate)
288                         # Run decoding sequence
289                         message(STATUS
290                                 "Decoding ${inFileAbs} to "
291                                 "${outFileAbsPath}...")
292                         file(REMOVE "${outFileAbsPath}")
293                         if(isHexFile LESS 0)
294                                 squirreljme_decode_file(BASE64
295                                         "${inFileAbs}" "${outFileAbsPath}")
296                         else()
297                                 squirreljme_decode_file(HEX
298                                         "${inFileAbs}" "${outFileAbsPath}")
299                         endif()
301                         # Store checksum
302                         squirreljme_write_file_checksum(
303                                 "${inFileAbs}" "${outFileAbsPath}")
304                 else()
305                         message(STATUS
306                                 "File ${outFileAbsPath} already decoded...")
307                 endif()
308         endforeach()
309 endfunction()
311 # Sourceize a single file
312 function(squirreljme_sourceize_file inputPath
313         outputCPath outputHPath)
314         # Get the base name of the input file
315         get_filename_component(inputPathBaseName
316                 "${inputPath}" NAME)
318         # Where is the encoder?
319         squirreljme_util(sourceizeExePath sourceize)
321         # Run the command
322         execute_process(COMMAND "${sourceizeExePath}"
323                         "${inputPathBaseName}" "C"
324                 INPUT_FILE "${inputPath}"
325                 OUTPUT_FILE "${outputCPath}"
326                 RESULT_VARIABLE sourceizeExitCode
327                 TIMEOUT 16)
328         execute_process(COMMAND "${sourceizeExePath}"
329                 "${inputPathBaseName}" "H"
330                 INPUT_FILE "${inputPath}"
331                 OUTPUT_FILE "${outputHPath}"
332                 RESULT_VARIABLE sourceizeExitCode
333                 TIMEOUT 16)
335         # Failed
336         if(sourceizeExitCode)
337                 message(FATAL_ERROR
338                         "Sourceize failed: ${sourceizeExitCode}.")
339         endif()
340 endfunction()
342 # Sourceize an entire directory
343 function(squirreljme_sourceize_dir inputDir outputDir)
344         # Encode all file accordingly
345         file(GLOB inFiles "${inputDir}/*")
346         foreach(inFile ${inFiles})
347                 # Determine the base name of the file
348                 get_filename_component(baseName
349                         "${inFile}" NAME)
351                 # Make sure the target directory exists first
352                 file(MAKE_DIRECTORY "${outputDir}")
354                 # Determine input and output
355                 get_filename_component(inFileAbs
356                         "${inFile}" ABSOLUTE)
357                 get_filename_component(outFileAbsPath
358                         "${outputDir}/${baseName}" ABSOLUTE)
360                 # Check if source file is up to date
361                 squirreljme_check_file_checksum(upToDate
362                         "${inFileAbs}" "${outFileAbsPath}.c")
363                 if(upToDate)
364                         # Do the same for the header file
365                         squirreljme_check_file_checksum(upToDate
366                                 "${inFileAbs}" "${outFileAbsPath}.h")
367                 endif()
369                 # Does decoding need to be rerun?
370                 if(NOT upToDate)
371                         # Run decoding sequence
372                         message(STATUS
373                                 "Sourceizing ${inFileAbs} to "
374                                 "${outFileAbsPath}.[ch]...")
375                         file(REMOVE "${outFileAbsPath}.c")
376                         file(REMOVE "${outFileAbsPath}.h")
377                         squirreljme_sourceize_file("${inFileAbs}"
378                                 "${outFileAbsPath}.c" "${outFileAbsPath}.h")
380                         # Store checksum
381                         squirreljme_write_file_checksum(
382                                 "${inFileAbs}" "${outFileAbsPath}.c")
383                         squirreljme_write_file_checksum(
384                                 "${inFileAbs}" "${outFileAbsPath}.h")
385                 else()
386                         message(STATUS
387                                 "File ${outFileAbsPath}.c already sourceized...")
388                 endif()
389         endforeach()
390 endfunction()