@@ -29,7 +29,7 @@ function(help)
29
29
set (ARG_MODE FATAL_ERROR)
30
30
endif ()
31
31
32
- cmake_path(APPEND DKSDK_DATA_HOME coder OUTPUT_VARIABLE DKCODER_HOME)
32
+ cmake_path(APPEND DKSDK_DATA_HOME coder h OUTPUT_VARIABLE DKCODER_HOME)
33
33
cmake_path(NATIVE_PATH DKCODER_HOME DKCODER_HOME_NATIVE)
34
34
35
35
message (${ARG_MODE} "usage: ./dk dksdk.coder.compile
@@ -43,11 +43,26 @@ downloaded and installed automatically.
43
43
Examples
44
44
========
45
45
46
- ./dk dksdk.coder.compile SOURCE expression .ml
47
- Creates `expression .cdi` in the same directory as `expression .ml`
46
+ ./dk dksdk.coder.compile EXPRESSION Expression .ml
47
+ Creates `Expression .cdi` in the same directory as `Expression .ml`.
48
48
49
- ./dk dksdk.coder.compile SOURCE expression.ml OUTPUT ../compiled.cdi
50
- Creates `../compiled.cdi`
49
+ ./dk dksdk.coder.compile EXPRESSION Expression.ml OUTPUT ../compiled.cdi
50
+ Creates `../compiled.cdi`.
51
+
52
+ ./dk dksdk.coder.compile EXPRESSION Expression.ml MODULES Extra.ml
53
+ Creates `Expression.cdi` where `Expression.ml` may use the module
54
+ named `Extra` defined by the contents of the file `Extra.ml`.
55
+
56
+ File Naming
57
+ ===========
58
+
59
+ Any `.ml` files must be specified with their first letter capitalized.
60
+ On case-sensitive filesystems (Linux, modern NTFS drives on Windows, but
61
+ not macOS and older FAT/FAT32 Windows drives) that means the `.ml`
62
+ files must be capitalized.
63
+
64
+ So name and refer to your files as `Expression.ml` (etc.) not
65
+ `expression.ml` (etc.).
51
66
52
67
Arguments
53
68
=========
58
73
QUIET
59
74
Do not print CMake STATUS messages.
60
75
61
- SOURCE filename
62
- The name of the file containing Coder expressions.
76
+ EXPRESSION filename
77
+ The path of the file containing Coder expressions. The path may
78
+ start with a tilde (~) which will be treated as the home
79
+ directory on Unix or the USERPROFILE directory on Windows.
80
+
81
+ The file MUST contain the `module E` and the `blocks` field at
82
+ minimum:
83
+
84
+ open DkSDKCoder_Std
85
+ module E (I : Cdinst.Lang.SYM) = struct
86
+ let blocks = []
87
+ end
88
+
89
+ That absolute minimum will not generate any source code. A more
90
+ realistic minimum is the following:
91
+
92
+ open DkSDKCoder_Std
93
+ module E (I : Cdinst.Lang.SYM) = struct
94
+ open I
95
+ let blocks = [
96
+ block
97
+ (label ~project:\" p\" ~deployment:\" d\" \" somename\" [])
98
+ [declare (typeregister \" Hi\" ) unit]
99
+ noop
100
+ ]
101
+ end
102
+
103
+ MODULES module_filenames
104
+ Optional list of module filenames that can be used by the EXPRESSION.
63
105
64
106
OUTPUT filename
65
107
The name of the output CDI file.
@@ -71,23 +113,157 @@ NO_SYSTEM_PATH
71
113
72
114
VERSION version
73
115
Use the version specified rather than the built-in ${DKCODER_COMPILE_VERSION}
74
- dkcoder version. CAUTION: Using this option causes the SHA-256 integrity checks
75
- to be skipped.
116
+ dkcoder version.
117
+
118
+ CAUTION: Using this option causes the SHA-256 integrity checks to be skipped.
119
+
120
+ CAUTION: If the version is a branch rather than a specific version number,
121
+ there is no way for `dksdk.coder.compile` to know about branch updates. The
122
+ branch version will never be updated once downloaded initially. You will need
123
+ to delete the branch at `${DKCODER_HOME_NATIVE} ` manually to overcome this
124
+ limitation.
125
+
126
+ Optimizations
127
+ =============
128
+
129
+ 1. The first time you run `dksdk.coder.compile` will download and install DkSDK
130
+ Coder cached on the [VERSION]. Subsequent times will not redownload nor
131
+ reinstall DkSDK Coder unless the [VERSION] is different.
132
+ 2. A compilation directory will be created and cached based on the absolute
133
+ path to the [EXPRESSION filename] and any [MODULES filenames]. That means first time
134
+ compilations for a EXPRESSION and MODULES may take a few seconds, but subsequent
135
+ compiles should be quick (assuming the EXPRESSION and MODULES are not terribly
136
+ complicated).
76
137
" )
77
138
endfunction ()
78
139
140
+ function (dkcoder_compile)
141
+ set (noValues)
142
+ set (singleValues EXPRESSION_PATH OUTPUT )
143
+ set (multiValues EXTRA_MODULE_PATHS)
144
+ cmake_parse_arguments (PARSE_ARGV 0 ARG "${noValues} " "${singleValues} " "${multiValues} " )
145
+
146
+ # Make absolute path to EXPRESSION, including expanding tilde (~)
147
+ file (REAL_PATH "${ARG_EXPRESSION_PATH} " expression_abspath EXPAND_TILDE)
148
+ dkcoder_get_validated_module_name(${expression_abspath} )
149
+
150
+ # EXPRESSION and EXECUTABLE_NAME is for dune.tmpl and to name a generated file below
151
+ set (EXPRESSION "${MODULE_NAME} " )
152
+ set (main_module "${EXPRESSION} Main" )
153
+ set (EXECUTABLE_NAME "${main_module} " )
154
+
155
+ # Convert EXTRA_MODULE_PATHS to absolute paths
156
+ set (all_modules "${EXPRESSION} " "${main_module} " )
157
+ set (extra_module_paths)
158
+ foreach (extra_module_path IN LISTS ARG_EXTRA_MODULE_PATHS)
159
+ file (REAL_PATH "${extra_module_path} " m_abspath EXPAND_TILDE)
160
+ dkcoder_get_validated_module_name(${m_abspath} )
161
+ list (APPEND all_modules "${MODULE_NAME} " )
162
+ list (APPEND extra_module_paths "${m_abspath} " )
163
+ endforeach ()
164
+
165
+ # MODULES is for dune.tmpl
166
+ list (JOIN all_modules " " MODULES)
167
+
168
+ # Read the footer
169
+ file (READ "${DKCODER_ETC} /Main.ml.tmpl" FOOTER_CONTENTS)
170
+
171
+ # Hash (which normalizes first) to make a compilation identifier
172
+ cmake_path(HASH expression_abspath pathhash)
173
+ set (compile_id_inputs ${pathhash} )
174
+ foreach (extra_module_path IN LISTS extra_module_paths)
175
+ cmake_path(HASH extra_module_path pathhash)
176
+ list (APPEND compile_id_inputs ${pathhash} )
177
+ endforeach ()
178
+ string (SHA256 COMPILE_ID "${compile_id_inputs} " )
179
+ string (SUBSTRING "${COMPILE_ID} " 0 8 COMPILE_ID)
180
+
181
+ # Compile directory
182
+ cmake_path(APPEND DKSDK_DATA_HOME coder c ${COMPILE_ID} OUTPUT_VARIABLE compile_dir)
183
+ file (MAKE_DIRECTORY "${compile_dir} " )
184
+
185
+ # Place template files into compile directory
186
+ file (COPY_FILE "${DKCODER_ETC} /dune-project.tmpl" "${compile_dir} /dune-project" ONLY_IF_DIFFERENT)
187
+ # Uses @EXECUTABLE_NAME@ and @MODULES@
188
+ configure_file ("${DKCODER_ETC} /dune.tmpl" "${compile_dir} /dune" @ONLY)
189
+ # Uses @EXPRESSION@
190
+ configure_file ("${DKCODER_ETC} /Main.ml.tmpl" "${compile_dir} /${main_module} .ml" @ONLY)
191
+ # The expression file itself.
192
+ file (CREATE_LINK "${expression_abspath} " "${compile_dir} /${EXPRESSION} .ml" COPY_ON_ERROR SYMBOLIC )
193
+ # And extra modules
194
+ foreach (extra_module_path IN LISTS extra_module_paths)
195
+ cmake_path(GET extra_module_path FILENAME m_filename)
196
+ file (CREATE_LINK "${extra_module_path} " "${compile_dir} /${m_filename} " COPY_ON_ERROR SYMBOLIC )
197
+ endforeach ()
198
+
199
+ # Make a findlib.conf
200
+ set (FINDLIB_PATH "${DKCODER_LIB} " )
201
+ set (FINDLIB_DESTDIR "${DKCODER_LIB} " )
202
+ if (CMAKE_HOST_WIN32 )
203
+ cmake_path(NATIVE_PATH FINDLIB_PATH FINDLIB_PATH)
204
+ cmake_path(NATIVE_PATH FINDLIB_DESTDIR FINDLIB_DESTDIR)
205
+ # Escape backslashes
206
+ string (REPLACE "\\ " "\\\\ " FINDLIB_PATH "${FINDLIB_PATH} " )
207
+ string (REPLACE "\\ " "\\\\ " FINDLIB_DESTDIR "${FINDLIB_DESTDIR} " )
208
+ endif ()
209
+ configure_file ("${CMAKE_CURRENT_FUNCTION_LIST_DIR} /../../__dk-tmpl/coder-findlib.conf" "${compile_dir} /findlib.conf" @ONLY)
210
+
211
+ # Build the bytecode executable
212
+ execute_process (
213
+ COMMAND
214
+
215
+ # Environment variables tested at dksdk-coder/ci/test-std-helper-dunebuild.sh
216
+ "${CMAKE_COMMAND} " -E env
217
+ "OCAMLLIB=${DKCODER_LIB} /ocaml"
218
+ "OCAMLFIND_CONF=${compile_dir} /findlib.conf"
219
+ "CAML_LD_LIBRARY_PATH=${DKCODER_LIB} /ocaml/stublibs"
220
+ --modify "PATH=path_list_prepend:${DKCODER_LIB} /ocaml/stublibs"
221
+ --modify "PATH=path_list_prepend:${DKCODER_BIN} "
222
+ --
223
+
224
+ "${DKCODER_DUNE} " build
225
+ --root "${compile_dir} "
226
+ --display=short
227
+ --no -buffer
228
+ --no -print-directory
229
+ --no -config
230
+ "${EXECUTABLE_NAME} .bc"
231
+ COMMAND_ERROR_IS_FATAL ANY
232
+ )
233
+
234
+ # Execute bytecode executable
235
+ execute_process (
236
+ COMMAND
237
+
238
+ # Environment variables tested at dksdk-coder/ci/test-std-helper-dunebuild.sh
239
+ "${CMAKE_COMMAND} " -E env
240
+ "CAML_LD_LIBRARY_PATH=${DKCODER_LIB} /ocaml/stublibs:${DKCODER_LIB} /stublibs"
241
+ --modify "PATH=path_list_prepend:${DKCODER_LIB} /stublibs"
242
+ --modify "PATH=path_list_prepend:${DKCODER_LIB} /ocaml/stublibs"
243
+ --
244
+
245
+ "${DKCODER_OCAMLRUN} " "${compile_dir} /_build/default/${EXECUTABLE_NAME} .bc" "${ARG_OUTPUT} "
246
+ COMMAND_ERROR_IS_FATAL ANY
247
+ )
248
+ endfunction ()
249
+
79
250
# Outputs:
80
251
# - DKCODER - location of dkcoder executable
252
+ # - DKCODER_BIN - location of bin directory
253
+ # - DKCODER_ETC - location of etc/dkcoder directory
254
+ # - DKCODER_LIB - location of lib/ directory containing lib/ocaml/ and other libraries compatible with dkcoder
81
255
# - DKCODER_OCAMLC - location of ocamlc compatible with dkcoder
82
256
# - DKCODER_OCAMLRUN - location of ocamlrun compatible with dkcoder
83
257
# - DKCODER_DUNE - location of dune compatible with dkcoder
84
- function (install_dkcoder )
258
+ function (dkcoder_install )
85
259
set (noValues NO_SYSTEM_PATH ENFORCE_SHA256)
86
260
set (singleValues VERSION )
87
261
set (multiValues)
88
262
cmake_parse_arguments (PARSE_ARGV 0 ARG "${noValues} " "${singleValues} " "${multiValues} " )
89
263
90
- cmake_path(APPEND DKSDK_DATA_HOME coder OUTPUT_VARIABLE DKCODER_HOME)
264
+ # Make a DkSDK Coder home
265
+ cmake_path(APPEND DKSDK_DATA_HOME coder h ${ARG_VERSION} OUTPUT_VARIABLE DKCODER_HOME)
266
+
91
267
set (hints ${DKCODER_HOME} /bin)
92
268
set (find_program_INITIAL)
93
269
if (ARG_NO_SYSTEM_PATH)
@@ -170,11 +346,50 @@ function(install_dkcoder)
170
346
find_program (DKCODER NAMES dkcoder REQUIRED HINTS ${hints} )
171
347
endif ()
172
348
173
- # ocamlc, ocamlrun and dune must be in the same directory as dkcoder.
174
349
cmake_path(GET DKCODER PARENT_PATH dkcoder_bindir)
350
+ cmake_path(GET dkcoder_bindir PARENT_PATH dkcoder_rootdir)
351
+
352
+ # ocamlc, ocamlrun and dune must be in the same directory as dkcoder.
175
353
find_program (DKCODER_OCAMLC NAMES ocamlc REQUIRED NO_DEFAULT_PATH HINTS ${dkcoder_bindir} )
176
354
find_program (DKCODER_OCAMLRUN NAMES ocamlrun REQUIRED NO_DEFAULT_PATH HINTS ${dkcoder_bindir} )
177
355
find_program (DKCODER_DUNE NAMES dune REQUIRED NO_DEFAULT_PATH HINTS ${dkcoder_bindir} )
356
+
357
+ # bin
358
+ cmake_path(APPEND dkcoder_rootdir bin OUTPUT_VARIABLE dkcoder_bin)
359
+ if (NOT IS_DIRECTORY "${dkcoder_bin} " )
360
+ message (FATAL_ERROR "Expected ${dkcoder_bin} to be present" )
361
+ endif ()
362
+ set (DKCODER_BIN "${dkcoder_bin} " PARENT_SCOPE)
363
+
364
+ # etc/dkcoder
365
+ cmake_path(APPEND dkcoder_rootdir etc dkcoder OUTPUT_VARIABLE dkcoder_etc)
366
+ if (NOT IS_DIRECTORY "${dkcoder_etc} " )
367
+ message (FATAL_ERROR "Expected ${dkcoder_etc} to be present" )
368
+ endif ()
369
+ set (DKCODER_ETC "${dkcoder_etc} " PARENT_SCOPE)
370
+
371
+ # lib
372
+ cmake_path(APPEND dkcoder_rootdir lib OUTPUT_VARIABLE dkcoder_lib)
373
+ if (NOT IS_DIRECTORY "${dkcoder_lib} " )
374
+ message (FATAL_ERROR "Expected ${dkcoder_lib} to be present" )
375
+ endif ()
376
+ set (DKCODER_LIB "${dkcoder_lib} " PARENT_SCOPE)
377
+ endfunction ()
378
+
379
+ # Output - MODULE_NAME
380
+ function (dkcoder_get_validated_module_name path )
381
+ if (path STREQUAL "" )
382
+ message (FATAL_ERROR "The path to the module is required." )
383
+ endif ()
384
+ cmake_path(GET path FILENAME name )
385
+ string (TOUPPER "${name} " name_upper)
386
+ string (SUBSTRING "${name} " 0 1 first_char)
387
+ string (SUBSTRING "${name_upper} " 0 1 first_char_upper)
388
+ if (NOT first_char STREQUAL first_char_upper)
389
+ message (FATAL_ERROR "The name of a module must start with a capital letter. Instead '${name} ' was used." )
390
+ endif ()
391
+ cmake_path(REMOVE_EXTENSION name OUTPUT_VARIABLE module_name)
392
+ set (MODULE_NAME "${module_name} " PARENT_SCOPE)
178
393
endfunction ()
179
394
180
395
function (run)
@@ -184,8 +399,8 @@ function(run)
184
399
set (CMAKE_CURRENT_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR} /${CMAKE_CURRENT_FUNCTION} " )
185
400
186
401
set (noValues HELP QUIET NO_SYSTEM_PATH)
187
- set (singleValues VERSION )
188
- set (multiValues)
402
+ set (singleValues VERSION EXPRESSION OUTPUT )
403
+ set (multiValues MODULES )
189
404
cmake_parse_arguments (PARSE_ARGV 0 ARG "${noValues} " "${singleValues} " "${multiValues} " )
190
405
191
406
if (ARG_HELP)
@@ -209,12 +424,27 @@ function(run)
209
424
# VERSION
210
425
if (ARG_VERSION)
211
426
set (VERSION ${ARG_VERSION} )
212
- set (expand_ENFORCE_SHA256)
427
+ set (expand_ENFORCE_SHA256)
213
428
else ()
214
429
set (VERSION ${DKCODER_COMPILE_VERSION} )
215
430
set (expand_ENFORCE_SHA256 ENFORCE_SHA256)
216
431
endif ()
217
432
218
- install_dkcoder(VERSION ${VERSION} ${expand_NO_SYSTEM_PATH} ${expand_ENFORCE_SHA256} )
433
+ # EXPRESSION
434
+ if (NOT ARG_EXPRESSION)
435
+ help(MODE NOTICE)
436
+ message (NOTICE "Missing EXPRESSION argument" )
437
+ return ()
438
+ endif ()
439
+
440
+ # OUTPUT
441
+ if (ARG_OUTPUT)
442
+ set (OUTPUT ${ARG_OUTPUT} )
443
+ else ()
444
+ cmake_path(REPLACE_EXTENSION ARG_EXPRESSION LAST_ONLY .cdi OUTPUT_VARIABLE OUTPUT )
445
+ endif ()
446
+
447
+ dkcoder_install(VERSION ${VERSION} ${expand_NO_SYSTEM_PATH} ${expand_ENFORCE_SHA256} )
219
448
message (STATUS "dkcoder is at: ${DKCODER} " )
449
+ dkcoder_compile(EXPRESSION_PATH ${ARG_EXPRESSION} EXTRA_MODULE_PATHS ${ARG_MODULES} OUTPUT ${OUTPUT} )
220
450
endfunction ()
0 commit comments