-
Notifications
You must be signed in to change notification settings - Fork 1
/
__dk.cmake
1072 lines (961 loc) · 49.2 KB
/
__dk.cmake
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
##########################################################################
# File: dkcoder/__dk.cmake #
# #
# Copyright 2023 Diskuv, Inc. #
# #
# Licensed under the Open Software License version 3.0 #
# (the "License"); you may not use this file except in compliance #
# with the License. You may obtain a copy of the License at #
# #
# https://opensource.org/license/osl-3-0-php/ #
# #
##########################################################################
# Recommendation: Place this file in source control.
# Auto-generated by `./dk dkml.wrapper.upgrade` of dkcoder.
include(FetchContent)
# What is the current YYYY-MM-DD? Do bother any dynamic calculation of EOL
# like __DkRun_Env_EOL_YYYY_MM_DD.
string(TIMESTAMP now_YYYY_MM_DD "%Y-%m-%d" UTC)
# Map of DkCoder versions.
# The SHA256 checksums are all available from
# https://gitlab.com/diskuv/distributions/1.0/dksdk-coder/-/packages/21844308 (select
# the right <COMPILE_VERSION> version of course)
#
# EOL = End of Life
# EOG = End of Grace Period (6 months past EOL usually)
# EOL > EOG. EOL starts the warning to upgrade but continues. EOG errors out.
set(__DkRun_V0_1_COMPILE_VERSION 0.1.0-4)
set(__DkRun_V0_1_URL_BASE https://gitlab.com/api/v4/projects/52918795/packages/generic/stdexport/${__DkRun_V0_1_COMPILE_VERSION})
set(__DkRun_V0_1_SHA256_windows_x86_64 ef49d016468db34d39f6f0a90231d9ff15bb482ec3ba91d329646185d6ae9002)
set(__DkRun_V0_1_SHA256_windows_x86 cddc4ddd7a907769582e3593069f50fa21f04fedae3a8fecb5cfad8e01e725e9)
set(__DkRun_V0_1_SHA256_linux_x86_64 1573e460ea4805da255ecd981aa505881968b1ffc4f188c37c9da9273367ce04)
set(__DkRun_V0_1_SHA256_linux_x86 todo_v0_1_release)
set(__DkRun_V0_1_SHA256_darwin_x86_64 5ffd147bb4131c86dcffe9e186cc3459d4f73e0c1c1f19460dd053f21d49f5d0)
set(__DkRun_V0_1_SHA256_darwin_arm64 159826b6977a2ff9e70dd20abd71ca7b92d9753840a2b88fa40bab71b2c5ea00)
set(__DkRun_V0_1_EOL_YYYY_MM_DD "2024-03-30")
set(__DkRun_V0_1_EOG_YYYY_MM_DD "2024-09-30")
set(__DkRun_V0_2_COMPILE_VERSION 0.2.0-3)
set(__DkRun_V0_2_URL_BASE https://gitlab.com/api/v4/projects/52918795/packages/generic/stdexport/${__DkRun_V0_2_COMPILE_VERSION})
set(__DkRun_V0_2_SHA256_linux_x86_64 b66a7f1409ede57ff22ca5c9946d288e9eae0784a7dc9eede25833ed64ea8553)
set(__DkRun_V0_2_SHA256_linux_x86 todo_v0_2_release)
set(__DkRun_V0_2_SHA256_darwin_x86_64 f84140b717fbf5619d9f4eb4b0386b7af2c9c661b395e922ec76a082061bb150)
set(__DkRun_V0_2_SHA256_darwin_arm64 501797d9970d8170593e71545c10f72cde546c8a0b38090779ce7fbb8634f995)
set(__DkRun_V0_2_SHA256_windows_x86_64 a3b70a77106c9d7430d802f14ae77ee9a9d2e0ed153df2ce6ae7faeb264b5937)
set(__DkRun_V0_2_SHA256_windows_x86 db3201e1881128ab9fdc9bd0678be8f36276165012bafbb348aee253b97bdf1c)
set(__DkRun_V0_2_EOL_YYYY_MM_DD "2024-11-30")
set(__DkRun_V0_2_EOG_YYYY_MM_DD "2025-05-30")
set(__DkRun_V0_3_COMPILE_VERSION 0.3.0-2)
set(__DkRun_V0_3_URL_BASE https://gitlab.com/api/v4/projects/52918795/packages/generic/stdexport/${__DkRun_V0_3_COMPILE_VERSION})
set(__DkRun_V0_3_SHA256_linux_x86_64 04d27d923256637a3cb64e3161336f4db5e70d07cccce05f90764c43156cce1f)
set(__DkRun_V0_3_SHA256_linux_x86 todo_v0_3_release)
set(__DkRun_V0_3_SHA256_darwin_x86_64 8d34848f656fd58d21dfdee79ec3d14c6f581e1551f996ea5c3a4e2ff612517e)
set(__DkRun_V0_3_SHA256_darwin_arm64 664e927f0ca32fe92fce0ef734b0643979cf2ac89666ff57acfa2b94b7f6bf30)
set(__DkRun_V0_3_SHA256_windows_x86_64 d9154a8d63104caac5bc6a6fc527d57548723357e154b506195a9f338b83dde9)
set(__DkRun_V0_3_SHA256_windows_x86 ddae39b6c1519c71f1bdad6ea5b5eba46ddf4ae86580676ef450d0c94dbb44e8)
set(__DkRun_V0_3_EOL_YYYY_MM_DD "2024-12-30")
set(__DkRun_V0_3_EOG_YYYY_MM_DD "2025-05-30")
set(__DkRun_V0_4_COMPILE_VERSION 0.4.0-2)
set(__DkRun_V0_4_URL_BASE https://gitlab.com/api/v4/projects/52918795/packages/generic/stdexport/${__DkRun_V0_4_COMPILE_VERSION})
set(__DkRun_V0_4_SHA256_linux_x86_64 ec7964fc69963b18eefd9e47e154c69e19464ebac3f3e3df5a9e1e69e29acabe)
set(__DkRun_V0_4_SHA256_linux_x86 todo_v0_5_release)
set(__DkRun_V0_4_SHA256_darwin_x86_64 d7d0d1d5d4fd289dcf9eb2057869b4c170e97f481a78e63baeb62c03e34a7f39)
set(__DkRun_V0_4_SHA256_darwin_arm64 7525036f7dcc10b38118925350ddfffeb6185df316ac12822cdd50817288aa06)
set(__DkRun_V0_4_SHA256_windows_x86_64 394f897d7cfc791f115e5bf427c7f56e054b510cbc96e79bb3ecf8f669b0ecc0)
set(__DkRun_V0_4_SHA256_windows_x86 d0561ee1b49728b05e008df16146bd6e282199d40fd9e67c21f059eee3aa3469)
set(__DkRun_V0_4_EOL_YYYY_MM_DD "2025-06-30")
set(__DkRun_V0_4_EOG_YYYY_MM_DD "2025-12-30")
# `Env` is a valid DkCoder version if $DKRUN_ENV_URL_BASE exists. Typically it is a file:// URL.
set(__DkRun_Env_URL_BASE)
if(DEFINED ENV{DKRUN_ENV_URL_BASE})
set(__DkRun_Env_COMPILE_VERSION Env)
set(__DkRun_Env_URL_BASE "$ENV{DKRUN_ENV_URL_BASE}")
set(__DkRun_Env_EOL_YYYY_MM_DD "9999-06-30")
set(__DkRun_Env_EOG_YYYY_MM_DD "9999-12-31")
endif()
# Once a version is supported in [__DkRun_LTS_VERSIONS] it should be supported until _EOL_YYYY_MM_DD.
# The last LTS version is what ./dk uses by default, so keep this chronologically sorted
# by oldest to newest.
set(__DkRun_LTS_VERSIONS V0_1 V0_2 V0_3 V0_4)
list(GET __DkRun_LTS_VERSIONS -1 __dkrun_v_id) # ie. the latest Vx_y
# ocamlc.exe, ocamlrun.exe, ocamldep.exe, dune.exe, dkcoder.exe all are compiled with
# Visual Studio on Windows. That means they need the redistributable installed.
# https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170
function(__dkcoder_install_vc_redist)
set(noValues)
set(singleValues LOGLEVEL)
set(multiValues)
cmake_parse_arguments(PARSE_ARGV 0 ARG "${noValues}" "${singleValues}" "${multiValues}")
# Default LOGLEVEL
if(NOT ARG_LOGLEVEL)
set(ARG_LOGLEVEL "STATUS")
endif()
# On Windows CMAKE_HOST_SYSTEM_PROCESSOR = ENV:PROCESSOR_ARCHITECTURE
# Values: AMD64, IA64, ARM64, x86
# https://docs.microsoft.com/en-us/windows/win32/winprog64/wow64-implementation-details?redirectedfrom=MSDN#environment-variables
if(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL x86 OR CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL X86)
set(vcarch x86)
elseif(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL arm64 OR CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL ARM64)
set(vcarch arm64)
else()
set(vcarch x64)
endif()
# Do we need to install?
if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.24)
# The "correct" way is to check through
# reg query HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\14.0\VC\Runtimes\x64 /V Version
# (etc.) if there is a newer version. Confer
# https://learn.microsoft.com/en-us/cpp/windows/redistributing-visual-cpp-files?view=msvc-170#install-the-redistributable-packages
cmake_host_system_information(RESULT result
QUERY WINDOWS_REGISTRY "HKLM/SOFTWARE/Microsoft/VisualStudio/14.0/VC/Runtimes/${vcarch}"
VALUE Version)
# v14.38.33135.00 -> 14.38.33135.00
string(REGEX REPLACE "^v" "" result "${result}")
if(result VERSION_GREATER_EQUAL 14.38.33135.00) # Future? Embed version number inside DkCoder file tree.
message(${ARG_LOGLEVEL} "Re-using Visual C++ Redistributable v${result}")
return()
endif()
endif()
set(url "https://aka.ms/vs/17/release/vc_redist.${vcarch}.exe")
message(${ARG_LOGLEVEL} "Downloading Visual C++ Redistributable from ${url}")
file(DOWNLOAD ${url} ${CMAKE_CURRENT_BINARY_DIR}/vc_redist.exe)
execute_process(
# https://github.com/aaronparker/vcredist/blob/main/VcRedist/VisualCRedistributables.json
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/vc_redist.exe /install /passive /norestart
RESULT_VARIABLE vc_redist_errcode
)
if(vc_redist_errcode EQUAL 0)
message(${ARG_LOGLEVEL} "Installed Visual C++ Redistributable.")
elseif(vc_redist_errcode EQUAL 1638)
# Allow exit code 1638 which is the code that a newer vcredist is already
# installed. https://github.com/diskuv/dkml-installer-ocaml/issues/60
message(${ARG_LOGLEVEL} "A newer Visual C++ Redistributable was already installed.")
elseif(vc_redist_errcode EQUAL 3010)
# https://github.com/ScoopInstaller/Extras/blob/master/bucket/vcredist2022.json#L22. Do nothing since /norestart.
message(${ARG_LOGLEVEL} "Visual C++ Redistributable reports that your machine needs to be restarted.")
else()
message(FATAL_ERROR "Problem: Visual C++ Redistributable failed to install. Exit code ${vc_redist_errcode}\n\nSolution: Download and install the latest Visual C++ Redistributable for your hardware architecture at https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist")
endif()
endfunction()
function(__dkcoder_error_wrong_version problem_prefix)
list(JOIN __DkRun_LTS_VERSIONS " " lts_versions)
message(FATAL_ERROR "Problem: ${problem_prefix}The only long-term supported versions accepted by this script are: ${lts_versions}.\n\nSolution: Run `./dk dkml.wrapper.upgrade` in your Terminal without the backticks.")
endfunction()
function(__dkcoder_abi)
set(noValues)
set(singleValues ABI_VARIABLE)
set(multiValues)
cmake_parse_arguments(PARSE_ARGV 0 ARG "${noValues}" "${singleValues}" "${multiValues}")
# Detect ABI (todo: cache it somewhere)
set(solution "Solution: If you are a DkSDK subscriber and need a new platform, contact your DkSDK Support representative.")
if(CMAKE_HOST_WIN32)
# On Windows CMAKE_HOST_SYSTEM_PROCESSOR = ENV:PROCESSOR_ARCHITECTURE
# Values: AMD64, IA64, ARM64, x86
# https://docs.microsoft.com/en-us/windows/win32/winprog64/wow64-implementation-details?redirectedfrom=MSDN#environment-variables
if(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL x86 OR CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL X86)
set(dkml_host_abi windows_x86)
else()
set(dkml_host_abi windows_x86_64)
endif()
elseif(CMAKE_HOST_APPLE)
# https://stackoverflow.com/a/69853058 for true ABI, not Rosetta
execute_process(COMMAND /usr/sbin/sysctl -n machdep.cpu.brand_string
OUTPUT_VARIABLE brand_string
OUTPUT_STRIP_TRAILING_WHITESPACE
COMMAND_ERROR_IS_FATAL ANY)
if(brand_string MATCHES "Apple M")
set(dkml_host_abi darwin_arm64)
else()
execute_process(COMMAND /usr/bin/uname -m
OUTPUT_VARIABLE brand_string
OUTPUT_STRIP_TRAILING_WHITESPACE
COMMAND_ERROR_IS_FATAL ANY)
if(host_machine_type STREQUAL x86_64)
set(dkml_host_abi darwin_x86_64)
elseif(host_machine_type STREQUAL arm64)
set(dkml_host_abi darwin_arm64)
else()
message(FATAL_ERROR "Problem: Unfortunately, your macOS ${host_machine_type} platform is currently not supported by this download script. ${solution}")
endif()
endif()
elseif(CMAKE_HOST_LINUX)
execute_process(COMMAND uname -m
OUTPUT_VARIABLE host_machine_type
OUTPUT_STRIP_TRAILING_WHITESPACE
COMMAND_ERROR_IS_FATAL ANY)
if(host_machine_type STREQUAL x86_64)
set(dkml_host_abi linux_x86_64)
elseif(host_machine_type STREQUAL i686)
set(dkml_host_abi linux_x86)
else()
message(FATAL_ERROR "Problem: Your Linux ${host_machine_type} platform is currently not supported by this download script. ${solution}")
endif()
else()
message(FATAL_ERROR "Problem: DkCoder is only available on Windows, macOS and Linux. ${solution}")
endif()
set("${ARG_ABI_VARIABLE}" "${dkml_host_abi}" PARENT_SCOPE)
endfunction()
# Testing? Set SOURCE_DATE_EPOCH environment variable to a future epoch time in seconds.
function(__dkcoder_check_end_of_life)
set(noValues)
set(singleValues LOGLEVEL EOL EOG QUIET)
set(multiValues)
cmake_parse_arguments(PARSE_ARGV 0 ARG "${noValues}" "${singleValues}" "${multiValues}")
set(project_dir "${CMAKE_SOURCE_DIR}")
cmake_path(NATIVE_PATH project_dir project_dir_NATIVE)
if(${ARG_EOG} STRLESS now_YYYY_MM_DD)
message(FATAL_ERROR "
Problem: The `./dk` wrapper version is ${__dkrun_v_id} but the final
date to upgrade (${ARG_EOG}) has past.
DIDN'T WRITE THIS PROJECT? Ask the project author how to upgrade!
Often a convenient way to upgrade is:
cd ${project_dir_NATIVE}
git stash
git switch main
git pull --ff-only
git stash pop
ARE YOU THE SCRIPT AUTHOR? If so run wrapper.upgrade twice, test
your scripts, and then give it out to your users:
cd ${project_dir_NATIVE}
./dk dkml.wrapper.upgrade
./dk dkml.wrapper.upgrade
... test ...
git commit
")
endif()
if(NOT QUIET AND ${ARG_EOL} STRLESS now_YYYY_MM_DD)
# Pause no more than once a day!
set(prompted "${CMAKE_CURRENT_BINARY_DIR}/eol/${now_YYYY_MM_DD}")
set(pause_message "working")
if(NOT EXISTS "${prompted}")
set(pause_message "after a 1-min pause")
endif()
message(${ARG_LOGLEVEL} "
Problem: The `./dk` wrapper version ${__dkrun_v_id} past its end-of-life on
${ARG_EOL}. The scripts will continue ${pause_message} but
you must upgrade by ${ARG_EOG} or the scripts will stop working.
DIDN'T WRITE THIS PROJECT? Ask the project author how to upgrade!
Often a convenient way to upgrade is:
cd ${project_dir_NATIVE}
git stash
git switch main
git pull --ff-only
git stash pop
ARE YOU THE SCRIPT AUTHOR? If so run wrapper.upgrade twice, test
your scripts, and then give a new branch to your users:
cd ${project_dir_NATIVE}
git switch --create name_of_your_new_branch
./dk dkml.wrapper.upgrade
./dk dkml.wrapper.upgrade
... test ...
git commit
")
# Pause no more than once a day!
set(prompted "${CMAKE_CURRENT_BINARY_DIR}/eol/${now_YYYY_MM_DD}")
if(NOT EXISTS "${prompted}")
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/eol")
execute_process(COMMAND ${CMAKE_COMMAND} -E sleep 60)
file(TOUCH "${prompted}")
endif()
endif()
endfunction()
# Installs DkCoder project.
#
# Arguments:
# QUIET
# VERSION
# LOGLEVEL
# Read-only Filesystem Outputs: (never modify the files or mutate the directories. On macOS part of a Bundle)
# - DKCODER - location of the `dkcoder` executable
# - DKCODER_VERSION - dotted form of DkCoder like 0.2.0.1
# - DKCODER_RUN - location of the `DkCoder_Edge-Run.bc` bytecode executable (here "Edge" means the latest version for the VERSION; aka. the VERSION itself)
# - DKCODER_RUN_VERSION - Env or V0_2. Whatever was used to launch in `./dk DkRun_V0_2.Run` (etc.)
# - DKCODER_HELPERS - location of bin directory or DkCoder.bundle/Contents/Helpers on macOS
# - DKCODER_ETC - location of etc/dkcoder directory
# - DKCODER_SITELIB - location of lib/ directory containing lib/ocaml/ and other libraries compatible with dkcoder
# - DKCODER_SHARE - location of share directory
# - DKCODER_OCAMLC - location of ocamlc compatible with dkcoder
# - DKCODER_OCAMLRUN - location of ocamlrun compatible with dkcoder
# - DKCODER_DUNE - location of dune compatible with dkcoder
function(__dkcoder_install)
set(noValues)
set(singleValues ABI VERSION LOGLEVEL QUIET)
set(multiValues)
cmake_parse_arguments(PARSE_ARGV 0 ARG "${noValues}" "${singleValues}" "${multiValues}")
# Default LOGLEVEL
if(NOT ARG_LOGLEVEL)
set(ARG_LOGLEVEL "STATUS")
endif()
# Set the DkCoder home
cmake_path(APPEND DKSDK_DATA_HOME coder h ${ARG_VERSION} OUTPUT_VARIABLE DKCODER_HOME)
# Parse Major.Minor from VERSION
if(ARG_VERSION MATCHES "^([0-9]+)[.]([0-9]+)[.]")
set(version_major "${CMAKE_MATCH_1}")
set(version_minor "${CMAKE_MATCH_2}")
set(V_id "V${version_major}_${version_minor}")
elseif(ARG_VERSION STREQUAL "Env")
set(V_id "Env")
else()
# alert: do not expose unsanitized user-supplied data
message(FATAL_ERROR "Problem: We expected a full DkCoder version like 0.1.0-2.\n\nSolution: DkSDK subscribers should contact their DkSDK Support representative.")
endif()
# Check for EOL
set(eol "${__DkRun_${V_id}_EOL_YYYY_MM_DD}")
set(eog "${__DkRun_${V_id}_EOG_YYYY_MM_DD}")
__dkcoder_check_end_of_life(
LOGLEVEL "${ARG_LOGLEVEL}"
EOL "${eol}"
EOG "${eog}"
QUIET "${ARG_QUIET}")
# Get from DkCoder version map
set(url_base "${__DkRun_${V_id}_URL_BASE}")
set(compile_version "${__DkRun_${V_id}_COMPILE_VERSION}")
set(sha256_windows_x86_64 "${__DkRun_${V_id}_SHA256_windows_x86_64}")
set(sha256_windows_x86 "${__DkRun_${V_id}_SHA256_windows_x86}")
set(sha256_linux_x86_64 "${__DkRun_${V_id}_SHA256_linux_x86_64}")
set(sha256_linux_x86 "${__DkRun_${V_id}_SHA256_linux_x86}")
set(sha256_darwin_x86_64 "${__DkRun_${V_id}_SHA256_darwin_x86_64}")
set(sha256_darwin_arm64 "${__DkRun_${V_id}_SHA256_darwin_arm64}")
if(compile_version STREQUAL "Env")
if(NOT url_base)
__dkcoder_error_wrong_version("You were using the Env version of DkCoder. ")
endif()
elseif(NOT url_base OR NOT compile_version OR NOT sha256_windows_x86_64 OR NOT sha256_windows_x86
OR NOT sha256_linux_x86_64 OR NOT sha256_linux_x86
OR NOT sha256_darwin_x86_64 OR NOT sha256_darwin_arm64)
__dkcoder_error_wrong_version("You were using DkCoder version ${V_id}. ")
endif()
# Make a work directory
set(CMAKE_CURRENT_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/_dkcoder__${compile_version}")
# Get the ABI
set(dkml_host_abi "${ARG_ABI}")
# Location where ocamlfind.conf should be
if(DKCODER_VERSION VERSION_LESS_EQUAL 0.4.0.1)
set(ocamlfind_conf "${DKCODER_HOME}/findlib.conf")
endif()
set(hints "${DKCODER_HOME}/DkCoder.bundle/Contents/Helpers" "${DKCODER_HOME}/bin")
set(find_program_ARGS NO_DEFAULT_PATH)
find_program(DKCODER NAMES dkcoder HINTS ${hints} ${find_program_ARGS})
if(NOT DKCODER)
# Download into ${DKCODER_HOME} (which is one of the HINTS)
set(downloaded)
# URL
if(dkml_host_abi MATCHES "^windows_.*" OR dkml_host_abi MATCHES "^darwin_.*")
set(out_exp .zip)
else()
set(out_exp .tar.gz)
endif()
set(url "${url_base}/stdexport-${dkml_host_abi}${out_exp}")
# Checksum? Always do it unless we are using the Env version
set(expected_hash_ARGS)
if(compile_version STREQUAL "Env")
message(${ARG_LOGLEVEL} "Downloading DkCoder locally from ${url}")
else()
set(expected_hash_ARGS EXPECTED_HASH "SHA256=${sha256_${dkml_host_abi}}")
message(${ARG_LOGLEVEL} "Downloading and sha256 validating DkCoder from ${url}")
endif()
# Download
if(url MATCHES "^file://(.*)")
set(download_DEST "${CMAKE_MATCH_1}")
set(download_REMOVE OFF)
else()
set(download_DEST "${CMAKE_CURRENT_BINARY_DIR}/stdexport${out_exp}")
set(download_REMOVE ON)
file(DOWNLOAD "${url}" "${download_DEST}" ${expected_hash_ARGS})
endif()
# Clean
message(${ARG_LOGLEVEL} "Cleaning install location")
file(REMOVE_RECURSE "${DKCODER_HOME}")
file(MAKE_DIRECTORY "${DKCODER_HOME}")
# Extract
message(${ARG_LOGLEVEL} "Extracting DkCoder")
file(ARCHIVE_EXTRACT INPUT "${download_DEST}" DESTINATION "${DKCODER_HOME}")
# Install prereq: Visual C++ Redistributable
if(CMAKE_HOST_WIN32)
__dkcoder_install_vc_redist(LOGLEVEL ${ARG_LOGLEVEL})
endif()
# Post-install: Configure findlib.conf to point to macOS bundle or Unix/Win32 extraction
if(DKCODER_VERSION VERSION_LESS_EQUAL 0.4.0.1)
if(CMAKE_HOST_WIN32)
# Windows needs entries like: destdir="C:\\TARBALL\\lib"
cmake_path(NATIVE_PATH DKCODER_HOME DKCODER_HOME_NATIVE)
string(REPLACE "\\" "\\\\" DKCODER_HOME_NATIVE_ESCAPED "${DKCODER_HOME_NATIVE}")
file(CONFIGURE OUTPUT "${ocamlfind_conf}"
CONTENT [[destdir="@DKCODER_HOME_NATIVE_ESCAPED@\\lib"
path="@DKCODER_HOME_NATIVE_ESCAPED@\\lib"
stdlib="@DKCODER_HOME_NATIVE_ESCAPED@\\lib\\ocaml"]] @ONLY NEWLINE_STYLE UNIX)
elseif(CMAKE_HOST_APPLE)
file(CONFIGURE OUTPUT "${ocamlfind_conf}"
CONTENT [[destdir="@DKCODER_HOME@/DkCoder.bundle/Contents/Resources/lib"
path="@DKCODER_HOME@/DkCoder.bundle/Contents/Resources/lib"
stdlib="@DKCODER_HOME@/DkCoder.bundle/Contents/Resources/lib/ocaml"]] @ONLY NEWLINE_STYLE UNIX)
else()
file(CONFIGURE OUTPUT "${ocamlfind_conf}"
CONTENT [[destdir="@DKCODER_HOME@/lib"
path="@DKCODER_HOME@/lib"
stdlib="@DKCODER_HOME@/DkCoder.bundle/Contents/Resources/lib/ocaml"]] @ONLY NEWLINE_STYLE UNIX)
endif()
# Cleanup
message(${ARG_LOGLEVEL} "Cleaning DkCoder intermediate files")
file(REMOVE ${CMAKE_CURRENT_BINARY_DIR}/stdexport${out_exp})
find_program(DKCODER NAMES dkcoder REQUIRED HINTS ${hints} ${find_program_ARGS})
message(${ARG_LOGLEVEL} "DkCoder installed.")
endif()
endif()
cmake_path(GET DKCODER PARENT_PATH dkcoder_helpers)
# macOS requires a code signing separation between executables (including shared libraries)
# and non-executables (include bytecode). The latter will be in macOS bundle Resources/.
if(CMAKE_HOST_APPLE)
cmake_path(GET dkcoder_helpers PARENT_PATH dkcoder_resourcesdir)
cmake_path(APPEND dkcoder_resourcesdir Resources)
else()
cmake_path(GET dkcoder_helpers PARENT_PATH dkcoder_resourcesdir)
endif()
# Export binaries.
# ocamlc, ocamlrun and dune must be in the same directory as dkcoder.
find_program(DKCODER_OCAMLC NAMES ocamlc REQUIRED NO_DEFAULT_PATH HINTS ${dkcoder_helpers})
find_program(DKCODER_OCAMLRUN NAMES ocamlrun REQUIRED NO_DEFAULT_PATH HINTS ${dkcoder_helpers})
find_program(DKCODER_DUNE NAMES dune REQUIRED NO_DEFAULT_PATH HINTS ${dkcoder_helpers})
find_program(DKCODER_RUN NAMES DkCoder_Edge-Run.bc REQUIRED NO_DEFAULT_PATH HINTS ${dkcoder_resourcesdir}/bytecode)
set(problem_solution "Problem: The DkCoder installation is corrupted. Solution: Remove the directory ${DKCODER_HOME} and try again.")
# Export ocamlfind.conf
if(DKCODER_VERSION VERSION_LESS_EQUAL 0.4.0.1)
set(DKCODER_OCAMLFIND_CONF "${ocamlfind_conf}" PARENT_SCOPE)
endif()
# Export bin/ or macOS bundle Helpers/
if(NOT IS_DIRECTORY "${dkcoder_helpers}")
message(FATAL_ERROR "${problem_solution}")
endif()
set(DKCODER_HELPERS "${dkcoder_helpers}" PARENT_SCOPE)
# Export etc/dkcoder/ or macOS bundle Resources/etc/dkcoder
cmake_path(APPEND dkcoder_resourcesdir etc dkcoder OUTPUT_VARIABLE dkcoder_etc)
if(NOT IS_DIRECTORY "${dkcoder_etc}")
message(FATAL_ERROR "${problem_solution}")
endif()
set(DKCODER_ETC "${dkcoder_etc}" PARENT_SCOPE)
# Export lib
cmake_path(APPEND dkcoder_resourcesdir lib OUTPUT_VARIABLE dkcoder_sitelib)
if(NOT IS_DIRECTORY "${dkcoder_sitelib}")
message(FATAL_ERROR "${problem_solution}")
endif()
set(DKCODER_SITELIB "${dkcoder_sitelib}" PARENT_SCOPE)
# Export share
cmake_path(APPEND dkcoder_resourcesdir share OUTPUT_VARIABLE dkcoder_share)
if(NOT IS_DIRECTORY "${dkcoder_share}")
message(FATAL_ERROR "${problem_solution}")
endif()
set(DKCODER_SHARE "${dkcoder_share}" PARENT_SCOPE)
# Export version
string(REPLACE "-" "." compile_version_dotted "${compile_version}") # 0.2.0-1 -> 0.2.0.1
set(DKCODER_VERSION "${compile_version_dotted}" PARENT_SCOPE)
# Export run vid (Env or V0_1)
set(DKCODER_RUN_VERSION "${V_id}" PARENT_SCOPE)
endfunction()
macro(__dkcoder_prep_environment)
set(envMods_UNIX)
set(envMods_DOS)
set(envMods_CMAKE)
endmacro()
function(__dkcoder_add_environment_mod term) # macros can't handle backslashes
if(envMods_UNIX)
string(APPEND envMods_UNIX " ")
string(APPEND envMods_DOS " ")
endif()
string(APPEND envMods_DOS "--modify \"${term}\"")
string(APPEND envMods_UNIX "--modify '${term}'")
list(APPEND envMods_CMAKE --modify "${term}")
set(envMods_DOS "${envMods_DOS}" PARENT_SCOPE)
set(envMods_UNIX "${envMods_UNIX}" PARENT_SCOPE)
set(envMods_CMAKE "${envMods_CMAKE}" PARENT_SCOPE)
endfunction()
function(__dkcoder_add_environment_set namevalue) # macros can't handle backslashes
if(envMods_UNIX)
string(APPEND envMods_UNIX " ")
string(APPEND envMods_DOS " ")
endif()
string(APPEND envMods_DOS "\"${namevalue}\"")
string(APPEND envMods_UNIX "'${namevalue}'")
list(APPEND envMods_CMAKE "${namevalue}")
set(envMods_DOS "${envMods_DOS}" PARENT_SCOPE)
set(envMods_UNIX "${envMods_UNIX}" PARENT_SCOPE)
set(envMods_CMAKE "${envMods_CMAKE}" PARENT_SCOPE)
endfunction()
# Delegate to DkCoder. We don't run inside CMake since CMake intercepts signals and (related?) makes
# Dune watch mode hang on Windows.
# Confer: https://stackoverflow.com/questions/75071180/pass-ctrlc-to-cmake-custom-command-under-vscode
function(__dkcoder_delegate)
set(noValues)
set(singleValues PACKAGE_NAMESPACE PACKAGE_QUALIFIER FULLY_QUALIFIED_MODULE ARGUMENT_LIST_VARIABLE)
set(multiValues)
cmake_parse_arguments(PARSE_ARGV 0 ARG "${noValues}" "${singleValues}" "${multiValues}")
# Calculate environment variables
# Environment variables tested at dksdk-coder/ci/test-std-helper-dunebuild.sh.
# Since we have a rule that does [ocamlrun], which depends on
# compiling a bytecode executable, we have to do union of environments for
# both ocamlc + ocamlrun.
__dkcoder_prep_environment()
__dkcoder_add_environment_set("OCAMLLIB=${DKCODER_SITELIB}/ocaml")
if(DKCODER_VERSION VERSION_LESS_EQUAL 0.4.0.1)
# Assumptions.ocamlfind_configuration_available_to_ocaml_compiler_in_coder_run
__dkcoder_add_environment_set("OCAMLFIND_CONF=${DKCODER_OCAMLFIND_CONF}")
endif()
__dkcoder_add_environment_set("CDI_OUTPUT=${output_abspath}") # This environment variable is communication to `@gen-cdi` rule
# Assumptions.stublibs_are_available_to_ocaml_compiler_and_runtime_in_coder_run
# nit: Unclear why CAML_LD_LIBRARY_PATH is needed by Dune 3.12.1 when invoking [ocamlc] on Windows to get
# dllunix.dll (etc.), but it is. That is fine; we can do both PATH and CAML_LD_LIBRARY_PATH.
if(CMAKE_HOST_APPLE)
__dkcoder_add_environment_mod("CAML_LD_LIBRARY_PATH=path_list_prepend:${DKCODER_HELPERS}/stublibs")
__dkcoder_add_environment_mod("PATH=path_list_prepend:${DKCODER_HELPERS}/stublibs")
else()
__dkcoder_add_environment_mod("CAML_LD_LIBRARY_PATH=path_list_prepend:${DKCODER_SITELIB}/stublibs")
__dkcoder_add_environment_mod("CAML_LD_LIBRARY_PATH=path_list_prepend:${DKCODER_SITELIB}/ocaml/stublibs")
__dkcoder_add_environment_mod("PATH=path_list_prepend:${DKCODER_SITELIB}/stublibs")
__dkcoder_add_environment_mod("PATH=path_list_prepend:${DKCODER_SITELIB}/ocaml/stublibs")
endif()
# Assumptions.coder_run_has_environment_for_compiling_bytecode
#
# PATH=path_list_prepend? Assumptions.coder_compatible_dune_is_at_front_of_coder_run_path
__dkcoder_add_environment_mod("PATH=path_list_prepend:${DKCODER_HELPERS}")
if(DKCODER_VERSION VERSION_GREATER 0.2.0.1 OR DKCODER_VERSION STREQUAL Env)
__dkcoder_add_environment_set("DKCODER_CMAKE_EXE=${CMAKE_COMMAND}")
__dkcoder_add_environment_set("DKCODER_NINJA_EXE=${CMAKE_MAKE_PROGRAM}")
endif()
# Propagate DKCODER_SHARE and DKCODER_HELPERS and DKCODER_RUN_VERSION.
# Why not DKCODER_HOST_ABI? DkRun has a hardcoded default (so ABI hardcoding comes from the downloaded DkRun
# which is chosen by ./dk). But we don't change the default since a future DkRun may have a better
# detection of ABI (ex. ./dk downloads x86_64 for macOS but ABI is detected as arm64).
cmake_path(NATIVE_PATH DKCODER_HELPERS NORMALIZE DKCODER_HELPERS_NATIVE)
cmake_path(NATIVE_PATH DKCODER_SHARE NORMALIZE DKCODER_SHARE_NATIVE)
cmake_path(NATIVE_PATH DKCODER_SITELIB NORMALIZE DKCODER_SITELIB_NATIVE)
__dkcoder_add_environment_set("DKCODER_HELPERS=${DKCODER_HELPERS_NATIVE}")
__dkcoder_add_environment_set("DKCODER_SHARE=${DKCODER_SHARE_NATIVE}")
if(DKCODER_VERSION VERSION_GREATER 0.4.0.1 OR DKCODER_VERSION STREQUAL Env)
__dkcoder_add_environment_set("DKCODER_SITELIB=${DKCODER_SITELIB_NATIVE}")
endif()
__dkcoder_add_environment_set("DKCODER_RUN_VERSION=${DKCODER_RUN_VERSION}")
__dkcoder_add_environment_set("DKCODER_RUN_ENV_URL_BASE=${__DkRun_Env_URL_BASE}")
__dkcoder_add_environment_set("DKCODER_PWD=${DKCODER_PWD}")
# Console
__dkcoder_add_environment_set("DKCODER_TTY=${DKCODER_TTY}")
# Calculate command line arguments
set(dkcoder_ARGS)
foreach(arg IN LISTS "${ARG_ARGUMENT_LIST_VARIABLE}")
if(CMAKE_HOST_WIN32)
# escape double-quotes
string(REPLACE "\"" "\\\"" arg "${arg}")
string(APPEND dkcoder_ARGS " \"${arg}\"")
else()
# replace single quotes with: close string, add escaped single quote, re-open string
string(REPLACE "'" "'\\''" arg "${arg}")
string(APPEND dkcoder_ARGS " '${arg}'")
endif()
endforeach()
# Find out which is the entry bytecode executable.
if(ARG_PACKAGE_NAMESPACE STREQUAL Dk AND ARG_PACKAGE_QUALIFIER STREQUAL Run)
if(ARG_FULLY_QUALIFIED_MODULE STREQUAL Run)
set(entryExec "${DKCODER_RUN}")
else()
message(FATAL_ERROR "Problem: DkCoder only supports the Run entrypoint. Solution: Was there a typo? Try DkRun_${__dkrun_v_id}.Run instead.")
endif()
else()
# If not explicitly a built-in DkCoder entry then use the [Run] entry.
set(entryExec "${DKCODER_RUN}")
# We also know that no options will be supplied to the [Run] entry
# so we can simplify the user experience slightly by adding in the [--]
# on their behalf.
# So `./dk MyLibrary_Std.Blah --help` should print help.
# In contrast, using versioned run requires:
# `./dk DkRun_Vm_n.Run -- MyLibrary_Std.Blah --help`
# but you can also do:
# `./dk DkRun_Vm_n.Run --help`
# `./dk DkRun_Vm_n.Run --log-level INFO -- MyLibrary_Std.Blah`
string(PREPEND dkcoder_ARGS "-- ")
endif()
# Write postscript launch script.
if(CMAKE_HOST_WIN32)
cmake_path(NATIVE_PATH CMAKE_COMMAND CMAKE_COMMAND_NATIVE)
cmake_path(NATIVE_PATH DKCODER_OCAMLRUN DKCODER_OCAMLRUN_NATIVE)
cmake_path(NATIVE_PATH entryExec entryExec_NATIVE)
file(CONFIGURE OUTPUT "${DKCODER_POST_SCRIPT}" CONTENT [[REM @ECHO OFF
REM Clear "SET" variables from dk.cmd. They are not part of DkCoder API.
SET DK_7Z_MAJVER=
SET DK_7Z_MINVER=
SET DK_7Z_DOTVER=
SET DK_7Z_VER=
SET DK_CMAKE_VER=
SET DK_NINJA_VER=
SET DK_BUILD_TYPE=
SET DK_SHARE=
SET DK_PROJ_DIR=
SET DK_PWD=
SET DK_CKSUM_7ZR=
SET DK_CKSUM_7ZEXTRA=
SET DK_CKSUM_CMAKE=
SET DK_CKSUM_NINJA=
SET DK_WORKDIR=
SET DK_NONCE=
SET DK_NINJA_EXE=
SET DK_CMDLINE=
SET DK_CMAKE_EXE=
SET DK_TTY=
REM Clear variables that influence __dk.cmake. They are not part of DkCoder API.
SET DKRUN_ENV_URL_BASE=
"@CMAKE_COMMAND_NATIVE@" -E env @envMods_DOS@ -- "@DKCODER_OCAMLRUN_NATIVE@" "@entryExec_NATIVE@" @dkcoder_ARGS@
]]
@ONLY NEWLINE_STYLE DOS)
else()
# + Clear "export" variables from dk
file(CONFIGURE OUTPUT "${DKCODER_POST_SCRIPT}" CONTENT [[#!/bin/sh
set -euf
# Clear "export" variables from ./dk. They are not part of DkCoder API.
unset DKMLSYS_MV DKMLSYS_CHMOD DKMLSYS_UNAME DKMLSYS_ENV DKMLSYS_AWK DKMLSYS_SED DKMLSYS_COMM DKMLSYS_INSTALL
unset DKMLSYS_RM DKMLSYS_SORT DKMLSYS_CAT DKMLSYS_STAT DKMLSYS_GREP DKMLSYS_CURL DKMLSYS_WGET DKMLSYS_TR
unset DK_PROG_INSTALLED_LOCATION
# Clear variables that influence __dk.cmake. They are not part of DkCoder API.
unset DKRUN_ENV_URL_BASE
exec '@CMAKE_COMMAND@' -E env @envMods_DOS@ -- '@DKCODER_OCAMLRUN@' '@entryExec@' @dkcoder_ARGS@
]]
@ONLY NEWLINE_STYLE UNIX)
endif()
endfunction()
function(__parse_if_ocaml_command)
set(noValues)
set(singleValues COMMAND SUCCESS_VARIABLE
PACKAGE_NAMESPACE_VARIABLE PACKAGE_QUALIFIER_VARIABLE LIBRARY_VARIABLE
FULLY_QUALIFIED_MODULE_VARIABLE PRE_ARGUMENTS_VARIABLE)
set(multiValues)
cmake_parse_arguments(PARSE_ARGV 0 ARG "${noValues}" "${singleValues}" "${multiValues}")
# Format: PackageName_Libraryname(.Modulename)+
# Regex: ([A-Z][a-z][a-z0-9]*)([A-Z][A-Za-z0-9]*)_([A-Z][A-Za-z0-9_]*)([.][A-Z]([A-Za-z0-9_]*))+
# Confer: https://diskuv.com/dksdk/run/2024-intro-scripting/
string(LENGTH "${ARG_COMMAND}" command_LEN)
if(command MATCHES "^([A-Z][a-z][a-z0-9]*)([A-Z][A-Za-z0-9]*)_([A-Z][A-Za-z0-9_]*)(([.][A-Z]([A-Za-z0-9_]*))+)$")
# 1. Fully qualfied mode
set(${ARG_PACKAGE_NAMESPACE_VARIABLE} "${CMAKE_MATCH_1}" PARENT_SCOPE)
set(${ARG_PACKAGE_QUALIFIER_VARIABLE} "${CMAKE_MATCH_2}" PARENT_SCOPE)
set(${ARG_LIBRARY_VARIABLE} "${CMAKE_MATCH_3}" PARENT_SCOPE)
# Change .Run to Run
set(fqn "${CMAKE_MATCH_4}")
string(REGEX REPLACE "^[.]" "" fqn "${fqn}")
set(${ARG_FULLY_QUALIFIED_MODULE_VARIABLE} "${fqn}" PARENT_SCOPE)
set(${ARG_SUCCESS_VARIABLE} ON PARENT_SCOPE)
set(${ARG_PRE_ARGUMENTS_VARIABLE} "" PARENT_SCOPE)
return()
elseif(EXISTS "${command}" AND command MATCHES "[.]ml$")
# 2. Command-as-path mode
# If the command is a .ml file located _under_ the ./dk directory (for safety), then it can be run.
# Symlinks are not allowed to escape beyond the ./dk folder ... we are in a convenience mode that
# has a canonical alternative, so we can and should make this mode more restrictive.
file(REAL_PATH "${CMAKE_SOURCE_DIR}" baseReal)
file(REAL_PATH "${command}" commandReal)
cmake_path(IS_PREFIX baseReal "${commandReal}" isPrefix)
if(isPrefix)
set(${ARG_PACKAGE_NAMESPACE_VARIABLE} "Dk" PARENT_SCOPE)
set(${ARG_PACKAGE_QUALIFIER_VARIABLE} "Run" PARENT_SCOPE)
set(${ARG_LIBRARY_VARIABLE} "${__dkrun_v_id}" PARENT_SCOPE)
set(${ARG_FULLY_QUALIFIED_MODULE_VARIABLE} "Run" PARENT_SCOPE)
set(${ARG_SUCCESS_VARIABLE} ON PARENT_SCOPE)
set(${ARG_PRE_ARGUMENTS_VARIABLE} "--" "${command}" PARENT_SCOPE)
return()
else()
# Usability improvement when move to OCaml-based logic ... calculate what the fully-qualified module identifier
# would be, and then say what that fully-qualfiied module id resolves to on the filesystem. It will likely
# be different, so give options on how to add paths.
message(FATAL_ERROR "You can only use paths to OCaml scripts that are within the directory tree ${baseReal}. Consider using the fully-qualified module identifier of the script rather than its path.")
endif()
endif()
set(${ARG_SUCCESS_VARIABLE} OFF PARENT_SCOPE)
endfunction()
function(__parse_dkcoder_command_line)
# The first argument is <command>. All dots will be replaced with a
# triple underscore as a convenience and to be pretty for the user.
# However, we do not error if no <command> is given ... we'll do
# that later.
set(command)
set(expected_function_name)
set(quotedArgs "")
if(ARGC EQUAL 0 OR (ARGC EQUAL 1 AND ARGV0 STREQUAL HELP))
message(NOTICE [[Usage:
./dk <command> HELP
./dk <command> [args]
Environment variables:
DKCODER_TTL_MINUTES=<n>. How many minutes before updates are fetched.
Defaults to 60 minutes. Set to 0 to disable caching.
]])
else()
set(command ${ARGV0})
string(REPLACE "." "___" expected_function_name ${command})
message(VERBOSE "Searching for ${expected_function_name}")
# Parse the remainder of the arguments [args]
# * Use technique from [Professional CMake: A Practical Guide - Forwarding Command Arguments]
# to be able to forward arguments correctly to an inner function (the <command> function).
cmake_parse_arguments(PARSE_ARGV 1 FWD "" "" "")
foreach(arg IN LISTS FWD_UNPARSED_ARGUMENTS)
string(APPEND quotedArgs " [===[${arg}]===]")
endforeach()
endif()
# Set policies (we are in a new EVAL CODE context)
# Included scripts do automatic cmake_policy PUSH and POP
if(POLICY CMP0011)
cmake_policy(SET CMP0011 NEW)
endif()
# Allow GIT_SUBMODULES empty to mean no submodules
if(POLICY CMP0097)
cmake_policy(SET CMP0097 NEW)
endif()
# Setup the binary directory
if(NOT DKCODER_WORKDIR)
message(FATAL_ERROR "Problem: Your `dk` and/or `dk.cmd` are corrupted. Solution: Follow the instructions at https://github.com/diskuv/dkcoder.git#installing")
endif()
set(CMAKE_BINARY_DIR "${DKCODER_WORKDIR}")
set(CMAKE_CURRENT_BINARY_DIR "${CMAKE_BINARY_DIR}")
# Set a log level used for essentially informational messages
set(__dkcoder_log_level STATUS)
if(DEFINED ENV{DKCODER_LOGLEVEL} AND (
"$ENV{DKCODER_LOGLEVEL_OVERRIDE}" STREQUAL DEBUG OR
"$ENV{DKCODER_LOGLEVEL_OVERRIDE}" STREQUAL STATUS))
set(__dkcoder_log_level "$ENV{DKCODER_LOGLEVEL_OVERRIDE}")
endif()
# Determine if the <command> is a legacy CMake command or a modern OCaml command
__parse_if_ocaml_command(COMMAND "${command}"
SUCCESS_VARIABLE is_ocaml
PACKAGE_NAMESPACE_VARIABLE package_namespace
PACKAGE_QUALIFIER_VARIABLE package_qualifier
LIBRARY_VARIABLE library
FULLY_QUALIFIED_MODULE_VARIABLE module
PRE_ARGUMENTS_VARIABLE pre_arguments)
if(is_ocaml)
# Get COMPILE_VERSION. Simultaneously recreate the argument list.
# Argument list:
# ./dk DkRun_V0_1.Run DkHelloScript_Std.Example001 1 2 3
# ==> [DkHelloScript_Std.Example001 1 2 3]
# ./dk DkHelloScript_Std.Example001 1 2 3
# ==> [DkHelloScript_Std.Example001 1 2 3]
# ./dk somewhere/DkHelloScript_Std/Example001 1 2 3
# ==> [somewhere/DkHelloScript_Std/Example001 1 2 3]
#
# Is the explicit version specified? That is, DkRun_V0_1.Run (etc.)?
set(argument_list ${pre_arguments} ${FWD_UNPARSED_ARGUMENTS})
if(package_namespace STREQUAL "Dk" AND package_qualifier STREQUAL "Run")
set(__dkrun_v_id "${library}") # ex. V0_1
else()
# No it is not DkRun_<Verson>.*. So use the latest LTS version available to this ./dk invocation.
# The user can upgrade with `./dk dkml.wrapper.upgrade` to get newer versions when they
# become available.
list(GET __DkRun_LTS_VERSIONS -1 __dkrun_v_id) # ie. the latest Vx_y
# Add back in the <command>
list(PREPEND argument_list ${command})
endif()
set(__dkrun_compile_version "${__DkRun_${__dkrun_v_id}_COMPILE_VERSION}")
# Validation
if(library STREQUAL "Env" AND NOT DEFINED ENV{DKRUN_ENV_URL_BASE})
# alert: do not expose unsanitized user-supplied data
message(FATAL_ERROR "Problem: We want you to use the DKRUN_ENV_URL_BASE environment variable.\n\nSolution: DkSDK subscribers should contact their DkSDK Support representative.")
elseif(NOT __dkrun_compile_version)
__dkcoder_error_wrong_version("")
endif()
# Set log level if DkRun_*.RunQuiet. And then rename module from RunQuiet to Run.
set(quiet OFF)
if(package_namespace STREQUAL "Dk" AND package_qualifier STREQUAL "Run" AND module STREQUAL "RunQuiet")
set(__dkcoder_log_level DEBUG)
set(module Run)
set(quiet ON)
endif()
# Detect ABI
__dkcoder_abi(ABI_VARIABLE abi)
# Do DkCoder install
__dkcoder_install(
ABI "${abi}"
LOGLEVEL "${__dkcoder_log_level}"
VERSION "${__dkrun_compile_version}"
QUIET "${quiet}")
# Do DkCoder delegation
__dkcoder_delegate(
PACKAGE_NAMESPACE "${package_namespace}"
PACKAGE_QUALIFIER "${package_qualifier}"
FULLY_QUALIFIED_MODULE "${module}"
ARGUMENT_LIST_VARIABLE argument_list)
return()
endif()
# Search in all the (CMake) user scripts
set(dot_function_names)
file(GLOB_RECURSE command_files
LIST_DIRECTORIES FALSE
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/cmake/scripts
cmake/scripts/*.cmake)
foreach(command_file IN LISTS command_files)
# Exclude files and directories that start with: __dk-
if(command_file MATCHES "(^|/)__dk-")
continue()
endif()
# Normalize and lowercase
cmake_path(NORMAL_PATH command_file)
string(TOLOWER "${command_file}" command_file)
cmake_path(REMOVE_EXTENSION command_file OUTPUT_VARIABLE command_file_no_ext)
# Convert to list
string(REPLACE "/" ";" command_stems_no_namespace ${command_file_no_ext})
# Make a pretty description only for validation
set(pretty_stems ${command_stems_no_namespace})
list(TRANSFORM pretty_stems PREPEND "'")
list(TRANSFORM pretty_stems APPEND "'")
string(JOIN ", " pretty_stems_str ${pretty_stems})
string(REGEX REPLACE ",([^,]*)" " and \\1" pretty_stems_str "${pretty_stems_str}")
# Validate that only alphanumeric with underscores (but not the reserved three underscores)
string(REGEX MATCH "[/a-z0-9_]*" only_alphanum_and_underscores "${command_file_no_ext}")
if(NOT only_alphanum_and_underscores STREQUAL "${command_file_no_ext}")
message(WARNING "Ignoring user script ${CMAKE_CURRENT_SOURCE_DIR}/${command_file}.
The stems of the user script (${pretty_stems_str}) must only contain letters, numbers and underscores.")
continue()
endif()
string(FIND "${command_file_no_ext}" "___" reserved_underscores)
if(reserved_underscores GREATER_EQUAL 0)
message(WARNING "Ignoring user script ${CMAKE_CURRENT_SOURCE_DIR}/${command_file}.
No stem of the user script (${pretty_stems_str}) can contain a triple underscore ('___').")
continue()
endif()
# Translate dev/xxx.cmake to the "user" namespaced function name
# `user__dev__xxx` and `user.dev.xxx`.
set(command_stems ${command_stems_no_namespace})
list(PREPEND command_stems "user")
string(JOIN "___" command_function_name ${command_stems})
string(JOIN "." dot_function_name ${command_stems})
list(APPEND dot_function_names ${dot_function_name})
# In a new scope (to avoid a global, leaky namespace) register the function.
message(VERBOSE "Shimming ${command_function_name}")
cmake_language(EVAL CODE "
function(${command_function_name})
include(\"${CMAKE_CURRENT_SOURCE_DIR}/cmake/scripts/${command_file}\")
if(COMMAND run)
run(${quotedArgs})
else()
message(FATAL_ERROR [[The user script ${CMAKE_CURRENT_SOURCE_DIR}/cmake/scripts/${command_file} was missing:
function(run)
# Your user code
endfunction()
]])
endif()
endfunction()
")
endforeach()
# Include all the system scripts.
# - Since the system scripts come after the user scripts, the user scripts
# don't override the system scripts unless the user scripts use deferred
# hooks or redefine CMake built-in functions. Regardless, the user
# scripts are namespaced with `user__` prefix
if(NOT IS_DIRECTORY cmake/scripts/dksdk)
# If this project (ex. dkcoder) has the system scripts, it must
# have all of them. Otherwise we download the system scripts.
# But we don't want to download every time we run the script.
#
# The default, but explicit so we know where it is.
set(dkcoder_subbuild_dir "${CMAKE_CURRENT_BINARY_DIR}/dkcoder-subbuild")
# Also the default, but explicit since we don't always call FetchContent_Populate().
set(dkcoder_src_dir "${CMAKE_CURRENT_BINARY_DIR}/dkcoder-src")
# Prior downloads are fine if done within the last one hour.
set(ttl_MINUTES 60)
if(DEFINED ENV{DKCODER_TTL_MINUTES})
set(ttl_MINUTES "$ENV{DKCODER_TTL_MINUTES}")
endif()
string(TIMESTAMP now_EPOCHSECS "%s")
math(EXPR min_valid_EPOCHSECS "${now_EPOCHSECS} - 60*${ttl_MINUTES}")
set(tstamp_EPOCHSECS 0)
if(EXISTS "${dkcoder_subbuild_dir}/build.ninja")
file(TIMESTAMP "${dkcoder_subbuild_dir}/build.ninja" tstamp_EPOCHSECS "%s")
endif()
if(NOT tstamp_EPOCHSECS OR tstamp_EPOCHSECS LESS_EQUAL min_valid_EPOCHSECS)
# Cache miss. Time to update dkcoder.
FetchContent_Populate(dkcoder
QUIET
SOURCE_DIR "${dkcoder_src_dir}"
SUBBUILD_DIR "${dkcoder_subbuild_dir}"
GIT_REPOSITORY https://github.com/diskuv/dkcoder.git
GIT_TAG 1.0
# As of 3.25.3 the bug https://gitlab.kitware.com/cmake/cmake/-/issues/24578
# has still not been fixed. That means empty strings get removed.
# ExternalProject_Add(GIT_SUBMODULES) in dkcoder-subbuild/CMakeLists.txt
# means fetch all submodules.
# https://gitlab.kitware.com/cmake/cmake/-/issues/20579#note_734045
# has a workaround.
GIT_SUBMODULES cmake # Non-git-submodule dir that already exists
GIT_SUBMODULES_RECURSE OFF)
endif()
file(GLOB_RECURSE system_command_files
LIST_DIRECTORIES FALSE
RELATIVE ${dkcoder_src_dir}/cmake/scripts
${dkcoder_src_dir}/cmake/scripts/dkml/*.cmake
${dkcoder_src_dir}/cmake/scripts/dksdk/*.cmake)
foreach(command_file IN LISTS system_command_files)
# Normalize and lowercase
cmake_path(NORMAL_PATH command_file)
string(TOLOWER "${command_file}" command_file)
cmake_path(REMOVE_EXTENSION command_file OUTPUT_VARIABLE command_file_no_ext)
# Convert to list
string(REPLACE "/" ";" command_stems_no_namespace ${command_file_no_ext})
# Translate dksdk/xxx.cmake to the function name `dksdk__xxx` and `dksdk.xxx`
set(command_stems ${command_stems_no_namespace})
string(JOIN "___" command_function_name ${command_stems})
string(JOIN "." dot_function_name ${command_stems})
list(APPEND dot_function_names ${dot_function_name})
# In a new scope (to avoid a global, leaky namespace) register the function.
message(VERBOSE "Shimming ${command_function_name}")
cmake_language(EVAL CODE "