forked from qt/qtbase
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathQtPublicSbomHelpers.cmake
1718 lines (1466 loc) · 61.4 KB
/
QtPublicSbomHelpers.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
# Copyright (C) 2024 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
# Starts repo sbom generation.
# Should be called before any targets are added to the sbom.
#
# INSTALL_PREFIX should be passed a value like CMAKE_INSTALL_PREFIX or QT_STAGING_PREFIX.
# The default value is \${CMAKE_INSTALL_PREFIX}, which is evaluated at install time, not configure
# time.
# This default value is the /preferred/ value, to ensure using cmake --install . --prefix <path>
# works correctly for lookup of installed files during SBOM generation.
#
# INSTALL_SBOM_DIR should be passed a value like CMAKE_INSTALL_DATAROOTDIR or
# Qt's INSTALL_SBOMDIR.
# The default value is "sbom".
#
# SUPPLIER, SUPPLIER_URL, DOCUMENT_NAMESPACE, COPYRIGHTS are self-explanatory.
function(_qt_internal_sbom_begin_project)
# Allow opt out via an internal variable. Will be used in CI for repos like qtqa.
if(QT_INTERNAL_FORCE_NO_GENERATE_SBOM)
set(QT_GENERATE_SBOM OFF CACHE BOOL "Generate SBOM" FORCE)
endif()
if(NOT QT_GENERATE_SBOM)
return()
endif()
set(opt_args
QT_CPE
)
set(single_args
INSTALL_PREFIX
INSTALL_SBOM_DIR
LICENSE_EXPRESSION
SUPPLIER
SUPPLIER_URL
DOWNLOAD_LOCATION
DOCUMENT_NAMESPACE
VERSION
SBOM_PROJECT_NAME
QT_REPO_PROJECT_NAME
CPE
)
set(multi_args
COPYRIGHTS
)
cmake_parse_arguments(PARSE_ARGV 0 arg "${opt_args}" "${single_args}" "${multi_args}")
_qt_internal_validate_all_args_are_parsed(arg)
if(CMAKE_VERSION LESS_EQUAL "3.19")
if(QT_IGNORE_MIN_CMAKE_VERSION_FOR_SBOM)
message(STATUS
"Using CMake version older than 3.19, and QT_IGNORE_MIN_CMAKE_VERSION_FOR_SBOM was "
"set to ON. qt_attribution.json files will not be processed.")
else()
message(FATAL_ERROR
"Generating an SBOM requires CMake version 3.19 or newer. You can pass "
"-DQT_IGNORE_MIN_CMAKE_VERSION_FOR_SBOM=ON to try to generate the SBOM anyway, "
"but it is not officially supported, and the SBOM might be incomplete.")
endif()
endif()
# The ntia-conformance-checker insists that a SPDX document contain at least one
# relationship that DESCRIBES a package, and that the package contains the string
# "Package-" in the spdx id. boot2qt spdx seems to contain the same.
if(arg_SBOM_PROJECT_NAME)
_qt_internal_sbom_set_root_project_name("${arg_SBOM_PROJECT_NAME}")
else()
_qt_internal_sbom_set_root_project_name("${PROJECT_NAME}")
endif()
if(arg_QT_REPO_PROJECT_NAME)
_qt_internal_sbom_set_qt_repo_project_name("${arg_QT_REPO_PROJECT_NAME}")
else()
_qt_internal_sbom_set_qt_repo_project_name("${PROJECT_NAME}")
endif()
_qt_internal_sbom_get_root_project_name_for_spdx_id(repo_project_name_for_spdx_id)
_qt_internal_sbom_get_root_project_name_lower_case(repo_project_name_lowercase)
if(arg_SUPPLIER_URL)
set(repo_supplier_url "${arg_SUPPLIER_URL}")
else()
_qt_internal_sbom_get_default_supplier_url(repo_supplier_url)
endif()
# Manual override.
if(arg_VERSION)
set(QT_SBOM_GIT_VERSION "${arg_VERSION}")
set(QT_SBOM_GIT_VERSION_PATH "${arg_VERSION}")
set(QT_SBOM_GIT_HASH "") # empty on purpose, no source of info
set(QT_SBOM_GIT_HASH_SHORT "") # empty on purpose, no source of info
set(non_git_version "${arg_VERSION}")
else()
# Query git version info.
_qt_internal_find_git_package()
_qt_internal_query_git_version(
EMPTY_VALUE_WHEN_NOT_GIT_REPO
OUT_VAR_PREFIX __sbom_
)
set(QT_SBOM_GIT_VERSION "${__sbom_git_version}")
set(QT_SBOM_GIT_VERSION_PATH "${__sbom_git_version_path}")
set(QT_SBOM_GIT_HASH "${__sbom_git_hash}")
set(QT_SBOM_GIT_HASH_SHORT "${__sbom_git_hash_short}")
# Git version might not be available.
set(non_git_version "${QT_REPO_MODULE_VERSION}")
if(NOT QT_SBOM_GIT_VERSION)
set(QT_SBOM_GIT_VERSION "${non_git_version}")
endif()
if(NOT QT_SBOM_GIT_VERSION_PATH)
set(QT_SBOM_GIT_VERSION_PATH "${non_git_version}")
endif()
endif()
# Save the variables in a global property to later query them in other functions.
set_property(GLOBAL PROPERTY QT_SBOM_GIT_VERSION "${QT_SBOM_GIT_VERSION}")
set_property(GLOBAL PROPERTY QT_SBOM_GIT_VERSION_PATH "${QT_SBOM_GIT_VERSION_PATH}")
set_property(GLOBAL PROPERTY QT_SBOM_GIT_HASH "${QT_SBOM_GIT_HASH}")
set_property(GLOBAL PROPERTY QT_SBOM_GIT_HASH_SHORT "${QT_SBOM_GIT_HASH_SHORT}")
if(arg_DOCUMENT_NAMESPACE)
set(repo_spdx_namespace "${arg_DOCUMENT_NAMESPACE}")
else()
# Used in external refs, either URI + UUID or URI + checksum. We use git version for now
# which is probably not conformat to spec.
set(repo_name_and_version "${repo_project_name_lowercase}-${QT_SBOM_GIT_VERSION}")
set(repo_spdx_namespace
"${repo_supplier_url}/spdxdocs/${repo_name_and_version}")
endif()
if(non_git_version)
set(version_suffix "-${non_git_version}")
else()
set(version_suffix "")
endif()
if(arg_INSTALL_SBOM_DIR)
set(install_sbom_dir "${arg_INSTALL_SBOM_DIR}")
elseif(INSTALL_SBOMDIR)
set(install_sbom_dir "${INSTALL_SBOMDIR}")
else()
set(install_sbom_dir "sbom")
endif()
if(arg_INSTALL_PREFIX)
set(install_prefix "${arg_INSTALL_PREFIX}")
else()
# The variable is escaped, so it is evaluated during cmake install time, so that the value
# can be overridden with cmake --install . --prefix <path>.
set(install_prefix "\${CMAKE_INSTALL_PREFIX}")
endif()
set(repo_spdx_relative_install_path
"${arg_INSTALL_SBOM_DIR}/${repo_project_name_lowercase}${version_suffix}.spdx")
# Prepend DESTDIR, to allow relocating installed sbom. Needed for CI.
set(repo_spdx_install_path
"\$ENV{DESTDIR}${install_prefix}/${repo_spdx_relative_install_path}")
if(arg_LICENSE_EXPRESSION)
set(repo_license "${arg_LICENSE_EXPRESSION}")
else()
# Default to NOASSERTION for root repo SPDX packages, because we have some repos
# with multiple licenses and AND-ing them together will create a giant unreadable list.
# It's better to rely on the more granular package licenses.
set(repo_license "")
endif()
set(repo_license_option "")
if(repo_license)
set(repo_license_option "LICENSE" "${repo_license}")
endif()
if(arg_COPYRIGHTS)
list(JOIN arg_COPYRIGHTS "\n" arg_COPYRIGHTS)
set(repo_copyright "<text>${arg_COPYRIGHTS}</text>")
else()
_qt_internal_sbom_get_default_qt_copyright_header(repo_copyright)
endif()
if(arg_SUPPLIER)
set(repo_supplier "${arg_SUPPLIER}")
else()
_qt_internal_sbom_get_default_supplier(repo_supplier)
endif()
if(arg_CPE)
set(qt_cpe "${arg_CPE}")
elseif(arg_QT_CPE)
_qt_internal_sbom_get_cpe_qt_repo(qt_cpe)
else()
set(qt_cpe "")
endif()
if(arg_DOWNLOAD_LOCATION)
set(download_location "${arg_DOWNLOAD_LOCATION}")
else()
_qt_internal_sbom_get_qt_repo_source_download_location(download_location)
endif()
set(project_comment "")
_qt_internal_get_configure_line(configure_line)
if(configure_line)
set(configure_line_comment
"\n${repo_project_name_lowercase} was configured with:\n ${configure_line}\n")
string(APPEND project_comment "${configure_line_comment}")
endif()
_qt_internal_sbom_begin_project_generate(
OUTPUT "${repo_spdx_install_path}"
OUTPUT_RELATIVE_PATH "${repo_spdx_relative_install_path}"
${repo_license_option}
COPYRIGHT "${repo_copyright}"
SUPPLIER "${repo_supplier}" # This must not contain spaces!
SUPPLIER_URL "${repo_supplier_url}"
DOWNLOAD_LOCATION "${download_location}"
PROJECT "${repo_project_name_lowercase}"
PROJECT_COMMENT "${project_comment}"
PROJECT_FOR_SPDX_ID "${repo_project_name_for_spdx_id}"
NAMESPACE "${repo_spdx_namespace}"
CPE "${qt_cpe}"
OUT_VAR_PROJECT_SPDX_ID repo_project_spdx_id
)
set_property(GLOBAL PROPERTY _qt_internal_project_attribution_files "")
set_property(GLOBAL PROPERTY _qt_internal_sbom_repo_document_namespace
"${repo_spdx_namespace}")
set_property(GLOBAL PROPERTY _qt_internal_sbom_relative_installed_repo_document_path
"${repo_spdx_relative_install_path}")
set_property(GLOBAL PROPERTY _qt_internal_sbom_repo_project_name_lowercase
"${repo_project_name_lowercase}")
set_property(GLOBAL PROPERTY _qt_internal_sbom_install_prefix
"${arg_INSTALL_PREFIX}")
set_property(GLOBAL PROPERTY _qt_internal_sbom_project_spdx_id
"${repo_project_spdx_id}")
# Collect project licenses.
set(license_dirs "")
if(EXISTS "${PROJECT_SOURCE_DIR}/LICENSES")
list(APPEND license_dirs "${PROJECT_SOURCE_DIR}/LICENSES")
endif()
# Allow specifying extra license dirs via a variable. Useful for standalone projects
# like sqldrivers.
if(QT_SBOM_LICENSE_DIRS)
foreach(license_dir IN LISTS QT_SBOM_LICENSE_DIRS)
if(EXISTS "${license_dir}")
list(APPEND license_dirs "${license_dir}")
endif()
endforeach()
endif()
list(REMOVE_DUPLICATES license_dirs)
set(license_file_wildcard "LicenseRef-*.txt")
list(TRANSFORM license_dirs APPEND "/${license_file_wildcard}" OUTPUT_VARIABLE license_globs)
file(GLOB license_files ${license_globs})
foreach(license_file IN LISTS license_files)
get_filename_component(license_id "${license_file}" NAME_WLE)
_qt_internal_sbom_add_license(
LICENSE_ID "${license_id}"
LICENSE_PATH "${license_file}"
NO_LICENSE_REF_PREFIX
)
endforeach()
# Make sure that any system library dependencies that have been found via qt_find_package or
# _qt_internal_find_third_party_dependencies have their spdx id registered now.
_qt_internal_sbom_record_system_library_spdx_ids()
set_property(GLOBAL PROPERTY _qt_internal_sbom_repo_begin_called TRUE)
_qt_internal_sbom_setup_project_ops()
endfunction()
# Check various internal options to decide which sbom generation operations should be setup.
# Considered operations are generation of a JSON sbom, validation of the SBOM, NTIA checker, etc.
function(_qt_internal_sbom_setup_project_ops)
set(options "")
if(QT_SBOM_GENERATE_JSON OR QT_INTERNAL_SBOM_GENERATE_JSON OR QT_INTERNAL_SBOM_DEFAULT_CHECKS)
list(APPEND options GENERATE_JSON)
endif()
# Tring to generate the JSON might fail if the python dependencies are not available.
# The user can explicitly request to fail the build if dependencies are not found.
# error out. For internal options that the CI uses, we always want to fail the build if the
# deps are not found.
if(QT_SBOM_REQUIRE_GENERATE_JSON OR QT_INTERNAL_SBOM_GENERATE_JSON
OR QT_INTERNAL_SBOM_DEFAULT_CHECKS)
list(APPEND options GENERATE_JSON_REQUIRED)
endif()
if(QT_SBOM_VERIFY OR QT_INTERNAL_SBOM_VERIFY OR QT_INTERNAL_SBOM_DEFAULT_CHECKS)
list(APPEND options VERIFY_SBOM)
endif()
# Do the same requirement check for SBOM verification.
if(QT_SBOM_REQUIRE_VERIFY OR QT_INTERNAL_SBOM_VERIFY OR QT_INTERNAL_SBOM_DEFAULT_CHECKS)
list(APPEND options VERIFY_SBOM_REQUIRED)
endif()
if(QT_INTERNAL_SBOM_VERIFY_NTIA_COMPLIANT OR QT_INTERNAL_SBOM_DEFAULT_CHECKS)
list(APPEND options VERIFY_NTIA_COMPLIANT)
endif()
if(QT_INTERNAL_SBOM_SHOW_TABLE OR QT_INTERNAL_SBOM_DEFAULT_CHECKS)
list(APPEND options SHOW_TABLE)
endif()
if(QT_INTERNAL_SBOM_AUDIT OR QT_INTERNAL_SBOM_AUDIT_NO_ERROR)
list(APPEND options AUDIT)
endif()
if(QT_INTERNAL_SBOM_AUDIT_NO_ERROR)
list(APPEND options AUDIT_NO_ERROR)
endif()
if(QT_GENERATE_SOURCE_SBOM)
list(APPEND options GENERATE_SOURCE_SBOM)
endif()
if(QT_LINT_SOURCE_SBOM)
list(APPEND options LINT_SOURCE_SBOM)
endif()
if(QT_INTERNAL_LINT_SOURCE_SBOM_NO_ERROR)
list(APPEND options LINT_SOURCE_SBOM_NO_ERROR)
endif()
_qt_internal_sbom_setup_project_ops_generation(${options})
endfunction()
# Ends repo sbom project generation.
# Should be called after all relevant targets are added to the sbom.
# Handles registering sbom info for recorded system libraries and then creates the sbom build
# and install rules.
function(_qt_internal_sbom_end_project)
if(NOT QT_GENERATE_SBOM)
return()
endif()
# Now that we know which system libraries are linked against because we added all
# subdirectories, we can add the recorded system libs to the sbom.
_qt_internal_sbom_add_recorded_system_libraries()
# Run sbom finalization for targets that had it scheduled, but haven't run yet.
# This can happen when _qt_internal_sbom_end_project is called within the same
# subdirectory scope as where the targets are meant to be finalized, but that would be too late
# and the targets wouldn't be added to the sbom.
# This would mostly happen in user projects, and not Qt repos, because in Qt repos we afaik
# never create targets in the root cmakelists (aside from the qtbase Platform targets).
get_cmake_property(targets _qt_internal_sbom_targets_waiting_for_finalization)
if(targets)
foreach(target IN LISTS targets)
_qt_internal_finalize_sbom("${target}")
endforeach()
endif()
_qt_internal_sbom_end_project_generate()
# Clean up external document ref properties, because each repo needs to start from scratch
# in a top-level build.
get_cmake_property(known_external_documents _qt_known_external_documents)
set_property(GLOBAL PROPERTY _qt_known_external_documents "")
foreach(external_document IN LISTS known_external_documents)
set_property(GLOBAL PROPERTY _qt_known_external_documents_${external_document} "")
endforeach()
set_property(GLOBAL PROPERTY _qt_internal_sbom_repo_begin_called FALSE)
# Add configure-time dependency on project attribution files.
get_property(attribution_files GLOBAL PROPERTY _qt_internal_project_attribution_files)
list(REMOVE_DUPLICATES attribution_files)
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${attribution_files}")
endfunction()
# Automatically begins sbom generation for a qt git repo unless QT_SKIP_SBOM_AUTO_PROJECT is TRUE.
function(_qt_internal_sbom_auto_begin_qt_repo_project)
# Allow skipping auto generation of sbom project, in case it needs to be manually adjusted with
# extra parameters.
if(QT_SKIP_SBOM_AUTO_PROJECT)
return()
endif()
_qt_internal_sbom_begin_qt_repo_project()
endfunction()
# Sets up sbom generation for a qt git repo or qt-git-repo-sub-project (e.g. qtpdf in qtwebengine).
#
# In the case of a qt-git-repo-sub-project, the function expects the following options:
# - SBOM_PROJECT_NAME (e.g. QtPdf)
# - QT_REPO_PROJECT_NAME (e.g. QtWebEngine)
#
# Expects the following variables to always be set before the function call:
# - QT_STAGING_PREFIX
# - INSTALL_SBOMDIR
function(_qt_internal_sbom_begin_qt_repo_project)
set(opt_args "")
set(single_args
SBOM_PROJECT_NAME
QT_REPO_PROJECT_NAME
)
set(multi_args "")
cmake_parse_arguments(PARSE_ARGV 0 arg "${opt_args}" "${single_args}" "${multi_args}")
_qt_internal_validate_all_args_are_parsed(arg)
set(sbom_project_args "")
_qt_internal_forward_function_args(
FORWARD_APPEND
FORWARD_PREFIX arg
FORWARD_OUT_VAR sbom_project_args
FORWARD_OPTIONS
${opt_args}
FORWARD_SINGLE
${single_args}
FORWARD_MULTI
${multi_args}
)
_qt_internal_sbom_begin_project(
INSTALL_SBOM_DIR "${INSTALL_SBOMDIR}"
QT_CPE
${sbom_project_args}
)
endfunction()
# Automatically ends sbom generation for a qt git repo unless QT_SKIP_SBOM_AUTO_PROJECT is TRUE.
function(_qt_internal_sbom_auto_end_qt_repo_project)
# Allow skipping auto generation of sbom project, in case it needs to be manually adjusted with
# extra parameters.
if(QT_SKIP_SBOM_AUTO_PROJECT)
return()
endif()
_qt_internal_sbom_end_qt_repo_project()
endfunction()
# Endssbom generation for a qt git repo or qt-git-repo-sub-project.
function(_qt_internal_sbom_end_qt_repo_project)
_qt_internal_sbom_end_project()
endfunction()
# Helper to get purl parsing options.
macro(_qt_internal_get_sbom_purl_parsing_options opt_args single_args multi_args)
set(${opt_args}
NO_PURL
NO_DEFAULT_QT_PURL
PURL_USE_PACKAGE_VERSION
)
set(${single_args}
PURL_TYPE
PURL_NAMESPACE
PURL_NAME
PURL_VERSION
PURL_SUBPATH
PURL_VCS_URL
)
set(${multi_args}
PURL_QUALIFIERS
)
endmacro()
# Helper to get the purl variant option names that should be recongized by sbom functions like
# _qt_internal_sbom_add_target.
macro(_qt_internal_get_sbom_purl_add_target_options opt_args single_args multi_args)
set(${opt_args} "")
set(${single_args} "")
set(${multi_args}
PURL_QT_ARGS
PURL_3RDPARTY_UPSTREAM_ARGS
PURL_MIRROR_ARGS
PURL_QT_VALUES
PURL_3RDPARTY_UPSTREAM_VALUES
PURL_MIRROR_VALUES
)
endmacro()
# Helper to get purl options that should be forwarded from _qt_internal_sbom_add_target to
# _qt_internal_sbom_handle_purl_values.
macro(_qt_internal_get_sbom_purl_handling_options opt_args single_args multi_args)
set(${opt_args}
IS_QT_ENTITY_TYPE
)
set(${single_args}
SUPPLIER
TYPE
VERSION
)
set(${multi_args} "")
_qt_internal_get_sbom_purl_add_target_options(
purl_add_target_opt_args purl_add_target_single_args purl_add_target_multi_args)
list(APPEND ${opt_args} ${purl_add_target_opt_args})
list(APPEND ${single_args} ${purl_add_target_single_args})
list(APPEND ${multi_args} ${purl_add_target_multi_args})
endmacro()
# Helper to get the options that _qt_internal_sbom_add_target understands, but that are also
# a safe subset for qt_internal_add_module, qt_internal_extend_target, etc to understand.
macro(_qt_internal_get_sbom_add_target_common_options opt_args single_args multi_args)
set(${opt_args}
NO_CURRENT_DIR_ATTRIBUTION
NO_ATTRIBUTION_LICENSE_ID
NO_DEFAULT_QT_LICENSE
NO_DEFAULT_QT_LICENSE_ID_LIBRARIES
NO_DEFAULT_QT_LICENSE_ID_EXECUTABLES
NO_DEFAULT_DIRECTORY_QT_LICENSE
NO_DEFAULT_QT_COPYRIGHTS
NO_DEFAULT_QT_PACKAGE_VERSION
NO_DEFAULT_QT_SUPPLIER
SBOM_INCOMPLETE_3RD_PARTY_DEPENDENCIES
IS_QT_3RD_PARTY_HEADER_MODULE
)
set(${single_args}
PACKAGE_VERSION
FRIENDLY_PACKAGE_NAME
CPE_VENDOR
CPE_PRODUCT
LICENSE_EXPRESSION
QT_LICENSE_ID
DOWNLOAD_LOCATION
ATTRIBUTION_ENTRY_INDEX
SBOM_PACKAGE_COMMENT
)
set(${multi_args}
COPYRIGHTS
CPE
SBOM_DEPENDENCIES
ATTRIBUTION_FILE_PATHS
ATTRIBUTION_FILE_DIR_PATHS
SBOM_RELATIONSHIPS
)
_qt_internal_get_sbom_purl_add_target_options(
purl_add_target_opt_args purl_add_target_single_args purl_add_target_multi_args)
list(APPEND ${opt_args} ${purl_add_target_opt_args})
list(APPEND ${single_args} ${purl_add_target_single_args})
list(APPEND ${multi_args} ${purl_add_target_multi_args})
endmacro()
# Helper to get all known SBOM specific options, without the ones that qt_internal_add_module
# and similar functions understand, like LIBRARIES, INCLUDES, etc.
macro(_qt_internal_get_sbom_specific_options opt_args single_args multi_args)
set(${opt_args} "")
set(${single_args} "")
set(${multi_args} "")
_qt_internal_get_sbom_add_target_common_options(
common_opt_args common_single_args common_multi_args)
list(APPEND ${opt_args} ${common_opt_args})
list(APPEND ${single_args} ${common_single_args})
list(APPEND ${multi_args} ${common_multi_args})
_qt_internal_sbom_get_multi_config_single_args(multi_config_single_args)
list(APPEND ${single_args} ${multi_config_single_args})
endmacro()
# Helper to get the options that _qt_internal_sbom_add_target understands.
# Also used in qt_find_package_extend_sbom.
macro(_qt_internal_get_sbom_add_target_options opt_args single_args multi_args)
set(${opt_args}
NO_INSTALL
)
set(${single_args}
TYPE
)
set(${multi_args}
LIBRARIES
PUBLIC_LIBRARIES
)
_qt_internal_get_sbom_specific_options(
specific_opt_args specific_single_args specific_multi_args)
list(APPEND ${opt_args} ${specific_opt_args})
list(APPEND ${single_args} ${specific_single_args})
list(APPEND ${multi_args} ${specific_multi_args})
endmacro()
# Generate sbom information for a given target.
# Creates:
# - a SPDX package for the target
# - zero or more SPDX file entries for each installed binary file
# - each binary file entry gets a list of 'generated from source files' section
# - dependency relationships to other target packages
# - other relevant information like licenses, copyright, etc.
# For licenses, copyrights, these can either be passed as options, or read from qt_attribution.json
# files.
# For dependencies, these are either specified via options, or read from properties set on the
# target by qt_internal_extend_target.
function(_qt_internal_sbom_add_target target)
if(NOT QT_GENERATE_SBOM)
return()
endif()
_qt_internal_get_sbom_add_target_options(opt_args single_args multi_args)
cmake_parse_arguments(PARSE_ARGV 1 arg "${opt_args}" "${single_args}" "${multi_args}")
_qt_internal_validate_all_args_are_parsed(arg)
get_target_property(target_type ${target} TYPE)
# Mark the target as a Qt module for sbom processing purposes.
# Needed for non-standard targets like Bootstrap and QtLibraryInfo, that don't have a Qt::
# namespace prefix.
if(arg_TYPE STREQUAL QT_MODULE)
set_target_properties(${target} PROPERTIES _qt_sbom_is_qt_module TRUE)
endif()
set(project_package_options "")
_qt_internal_sbom_is_qt_entity_type("${arg_TYPE}" is_qt_entity_type)
_qt_internal_sbom_is_qt_3rd_party_entity_type("${arg_TYPE}" is_qt_3rd_party_entity_type)
if(arg_FRIENDLY_PACKAGE_NAME)
set(package_name_for_spdx_id "${arg_FRIENDLY_PACKAGE_NAME}")
else()
set(package_name_for_spdx_id "${target}")
endif()
set(package_comment "")
if(arg_SBOM_INCOMPLETE_3RD_PARTY_DEPENDENCIES)
string(APPEND package_comment
"Note: This package was marked as not listing all of its consumed 3rd party "
"dependencies.\nThus the licensing and copyright information might be incomplete.\n")
endif()
if(arg_SBOM_PACKAGE_COMMENT)
string(APPEND package_comment "${arg_SBOM_PACKAGE_COMMENT}\n")
endif()
# Record the target spdx id right now, so we can refer to it in later attribution targets
# if needed.
_qt_internal_sbom_record_target_spdx_id(${target}
TYPE "${arg_TYPE}"
PACKAGE_NAME "${package_name_for_spdx_id}"
OUT_VAR package_spdx_id
)
set(attribution_args
PARENT_TARGET "${target}"
)
if(is_qt_entity_type)
list(APPEND attribution_args CREATE_SBOM_FOR_EACH_ATTRIBUTION)
endif()
# Forward the sbom specific options when handling attribution files because those might
# create other sbom targets that need to inherit the parent ones.
_qt_internal_get_sbom_specific_options(sbom_opt_args sbom_single_args sbom_multi_args)
_qt_internal_forward_function_args(
FORWARD_APPEND
FORWARD_PREFIX arg
FORWARD_OUT_VAR attribution_args
FORWARD_OPTIONS
${sbom_opt_args}
FORWARD_SINGLE
${sbom_single_args}
FORWARD_MULTI
${sbom_multi_args}
)
if(NOT arg_NO_CURRENT_DIR_ATTRIBUTION
AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/qt_attribution.json")
list(APPEND attribution_args
ATTRIBUTION_FILE_PATHS "${CMAKE_CURRENT_SOURCE_DIR}/qt_attribution.json"
)
endif()
_qt_internal_sbom_handle_qt_attribution_files(qa ${attribution_args})
# Collect license expressions, but in most cases, each expression needs to be abided, so we
# AND the accumulated license expressions.
set(license_expression "")
if(arg_LICENSE_EXPRESSION)
set(license_expression "${arg_LICENSE_EXPRESSION}")
endif()
# For Qt entities, we have some special handling.
if(is_qt_entity_type AND NOT arg_NO_DEFAULT_QT_LICENSE AND NOT arg_QT_LICENSE_ID)
if(arg_TYPE STREQUAL "QT_TOOL" OR arg_TYPE STREQUAL "QT_APP")
if(QT_SBOM_DEFAULT_QT_LICENSE_ID_EXECUTABLES
AND NOT arg_NO_DEFAULT_QT_LICENSE_ID_EXECUTABLES)
# A repo might contain only the "gpl3" license variant as the default for all
# executables, so allow setting it at the repo level to avoid having to repeat it
# for each target.
_qt_internal_sbom_get_spdx_license_expression(
"${QT_SBOM_DEFAULT_QT_LICENSE_ID_EXECUTABLES}" qt_license_expression)
else()
# For tools and apps, we use the gpl exception variant by default.
_qt_internal_sbom_get_spdx_license_expression("QT_COMMERCIAL_OR_GPL3_WITH_EXCEPTION"
qt_license_expression)
endif()
elseif(QT_SBOM_DEFAULT_QT_LICENSE_ID_LIBRARIES
AND NOT arg_NO_DEFAULT_QT_LICENSE_ID_LIBRARIES)
# A repo might contain only the "gpl3" license variant as the default for all modules
# and plugins, so allow setting it at the repo level to avoid having to repeat it
# for each target.
_qt_internal_sbom_get_spdx_license_expression(
"${QT_SBOM_DEFAULT_QT_LICENSE_ID_LIBRARIES}" qt_license_expression)
else()
# Otherwise, for modules and plugins we use the default qt license.
_qt_internal_sbom_get_spdx_license_expression("QT_DEFAULT" qt_license_expression)
endif()
_qt_internal_sbom_join_two_license_ids_with_op(
"${license_expression}" "AND" "${qt_license_expression}"
license_expression)
endif()
# Some Qt entities might request a specific license from the subset that we usually use.
if(arg_QT_LICENSE_ID)
_qt_internal_sbom_get_spdx_license_expression("${arg_QT_LICENSE_ID}"
requested_license_expression)
_qt_internal_sbom_join_two_license_ids_with_op(
"${license_expression}" "AND" "${requested_license_expression}"
license_expression)
endif()
# Allow setting a license expression string per directory scope via a variable.
if(is_qt_entity_type AND QT_SBOM_LICENSE_EXPRESSION AND NOT arg_NO_DEFAULT_DIRECTORY_QT_LICENSE)
set(qt_license_expression "${QT_SBOM_LICENSE_EXPRESSION}")
_qt_internal_sbom_join_two_license_ids_with_op(
"${license_expression}" "AND" "${qt_license_expression}"
license_expression)
endif()
# Read a license expression from the attribution json file.
if(qa_license_id AND NOT arg_NO_ATTRIBUTION_LICENSE_ID)
if(NOT qa_license_id MATCHES "urn:dje:license")
_qt_internal_sbom_join_two_license_ids_with_op(
"${license_expression}" "AND" "${qa_license_id}"
license_expression)
else()
message(DEBUG
"Attribution license id contains invalid spdx license reference: ${qa_license_id}")
set(invalid_license_comment
" Attribution license ID with invalid spdx license reference: ")
string(APPEND invalid_license_comment "${qa_license_id}\n")
string(APPEND package_comment "${invalid_license_comment}")
endif()
endif()
if(license_expression)
list(APPEND project_package_options LICENSE_CONCLUDED "${license_expression}")
# For qt entities we know the license we provide, so we mark it as declared as well.
if(is_qt_entity_type)
list(APPEND project_package_options LICENSE_DECLARED "${license_expression}")
endif()
endif()
# Copyrights are additive, so we collect them from all sources that were found.
set(copyrights "")
if(arg_COPYRIGHTS)
list(APPEND copyrights "${arg_COPYRIGHTS}")
endif()
if(is_qt_entity_type AND NOT arg_NO_DEFAULT_QT_COPYRIGHTS)
_qt_internal_sbom_get_default_qt_copyright_header(qt_default_copyright)
if(qt_default_copyright)
list(APPEND copyrights "${qt_default_copyright}")
endif()
endif()
if(qa_copyrights)
list(APPEND copyrights "${qa_copyrights}")
endif()
if(copyrights)
list(JOIN copyrights "\n" copyrights)
list(APPEND project_package_options COPYRIGHT "<text>${copyrights}</text>")
endif()
set(package_version "")
if(arg_PACKAGE_VERSION)
set(package_version "${arg_PACKAGE_VERSION}")
elseif(is_qt_entity_type AND NOT arg_NO_DEFAULT_QT_PACKAGE_VERSION)
_qt_internal_sbom_get_default_qt_package_version(package_version)
elseif(qa_version)
set(package_version "${qa_version}")
endif()
if(package_version)
list(APPEND project_package_options VERSION "${package_version}")
endif()
set(supplier "")
if((is_qt_entity_type OR is_qt_3rd_party_entity_type)
AND NOT arg_NO_DEFAULT_QT_SUPPLIER)
_qt_internal_sbom_get_default_supplier(supplier)
endif()
if(supplier)
list(APPEND project_package_options SUPPLIER "Organization: ${supplier}")
endif()
set(download_location "")
if(arg_DOWNLOAD_LOCATION)
set(download_location "${arg_DOWNLOAD_LOCATION}")
elseif(is_qt_entity_type)
_qt_internal_sbom_get_qt_repo_source_download_location(download_location)
elseif(arg_TYPE STREQUAL "QT_THIRD_PARTY_MODULE" OR arg_TYPE STREQUAL "QT_THIRD_PARTY_SOURCES")
if(qa_download_location)
set(download_location "${qa_download_location}")
elseif(qa_homepage)
set(download_location "${qa_homepage}")
endif()
elseif(arg_TYPE STREQUAL "SYSTEM_LIBRARY")
# Try to get package url that was set using CMake's set_package_properties function.
# Relies on querying the internal global property name that CMake sets in its
# implementation.
get_cmake_property(target_url _CMAKE_${package_name_for_spdx_id}_URL)
if(target_url)
set(download_location "${target_url}")
endif()
if(NOT download_location AND qa_download_location)
set(download_location "${qa_download_location}")
endif()
endif()
if(download_location)
list(APPEND project_package_options DOWNLOAD_LOCATION "${download_location}")
endif()
_qt_internal_sbom_get_package_purpose("${arg_TYPE}" package_purpose)
list(APPEND project_package_options PURPOSE "${package_purpose}")
set(cpe_args "")
if(arg_CPE)
list(APPEND cpe_args CPE "${arg_CPE}")
endif()
if(arg_CPE_VENDOR AND arg_CPE_PRODUCT)
_qt_internal_sbom_compute_security_cpe(custom_cpe
VENDOR "${arg_CPE_VENDOR}"
PRODUCT "${arg_CPE_PRODUCT}"
VERSION "${package_version}")
list(APPEND cpe_args CPE "${custom_cpe}")
endif()
if(qa_cpes)
_qt_internal_sbom_replace_qa_placeholders(
VALUES ${qa_cpes}
VERSION "${package_version}"
OUT_VAR qa_cpes_replaced
)
list(APPEND cpe_args CPE "${qa_cpes_replaced}")
endif()
# Add the qt-specific CPE if the target is a Qt entity type, or if it's a 3rd party entity type
# without any CPE specified.
if(is_qt_entity_type OR (is_qt_3rd_party_entity_type AND NOT cpe_args))
_qt_internal_sbom_compute_security_cpe_for_qt(cpe_list)
list(APPEND cpe_args CPE "${cpe_list}")
endif()
if(cpe_args)
list(APPEND project_package_options ${cpe_args})
endif()
# Assemble arguments to forward to the function that handles purl options.
set(purl_args "")
_qt_internal_get_sbom_purl_add_target_options(purl_opt_args purl_single_args purl_multi_args)
_qt_internal_forward_function_args(
FORWARD_APPEND
FORWARD_PREFIX arg
FORWARD_OUT_VAR purl_args
FORWARD_OPTIONS
${purl_opt_args}
FORWARD_SINGLE
${purl_single_args}
TYPE
FORWARD_MULTI
${purl_multi_args}
)
if(supplier)
list(APPEND purl_args SUPPLIER "${supplier}")
endif()
if(package_version)
list(APPEND purl_args VERSION "${package_version}")
endif()
if(is_qt_entity_type)
list(APPEND purl_args IS_QT_ENTITY_TYPE)
endif()
if(qa_purls)
_qt_internal_sbom_replace_qa_placeholders(
VALUES ${qa_purls}
VERSION "${package_version}"
OUT_VAR qa_purls_replaced
)
list(APPEND purl_args PURL_3RDPARTY_UPSTREAM_VALUES "${qa_purls_replaced}")
endif()
list(APPEND purl_args OUT_VAR purl_package_options)
_qt_internal_sbom_handle_purl_values(${target} ${purl_args})
if(purl_package_options)
list(APPEND project_package_options ${purl_package_options})
endif()
if(is_qt_3rd_party_entity_type
OR arg_TYPE STREQUAL "SYSTEM_LIBRARY"
OR arg_TYPE STREQUAL "THIRD_PARTY_LIBRARY"
OR arg_TYPE STREQUAL "THIRD_PARTY_LIBRARY_WITH_FILES"
)
if(qa_attribution_name)
string(APPEND package_comment " Name: ${qa_attribution_name}\n")
endif()
if(qa_description)
string(APPEND package_comment " Description: ${qa_description}\n")
endif()
if(qa_qt_usage)
string(APPEND package_comment " Qt usage: ${qa_qt_usage}\n")
endif()
if(qa_chosen_attribution_file_path)
_qt_internal_sbom_map_path_to_reproducible_relative_path(relative_attribution_path
PATH "${qa_chosen_attribution_file_path}"
)
string(APPEND package_comment
" Information extracted from:\n ${relative_attribution_path}\n")
endif()
if(NOT "${qa_chosen_attribution_entry_index}" STREQUAL "")
string(APPEND package_comment
" Entry index: ${qa_chosen_attribution_entry_index}\n")
endif()
endif()
if(package_comment)
list(APPEND project_package_options COMMENT "<text>\n${package_comment}</text>")
endif()
_qt_internal_sbom_handle_target_dependencies("${target}"
SPDX_ID "${package_spdx_id}"
LIBRARIES "${arg_LIBRARIES}"
PUBLIC_LIBRARIES "${arg_PUBLIC_LIBRARIES}"
OUT_RELATIONSHIPS relationships
)
get_cmake_property(project_spdx_id _qt_internal_sbom_project_spdx_id)
list(APPEND relationships "${project_spdx_id} CONTAINS ${package_spdx_id}")
if(arg_SBOM_RELATIONSHIPS)
list(APPEND relationships "${arg_SBOM_RELATIONSHIPS}")
endif()
list(REMOVE_DUPLICATES relationships)
list(JOIN relationships "\nRelationship: " relationships)
list(APPEND project_package_options RELATIONSHIP "${relationships}")
_qt_internal_sbom_generate_add_package(
PACKAGE "${package_name_for_spdx_id}"
SPDXID "${package_spdx_id}"
CONTAINS_FILES
${project_package_options}
)
set(no_install_option "")
if(arg_NO_INSTALL)
set(no_install_option NO_INSTALL)
endif()
set(framework_option "")
if(APPLE AND NOT target_type STREQUAL "INTERFACE_LIBRARY")
get_target_property(is_framework ${target} FRAMEWORK)
if(is_framework)
set(framework_option "FRAMEWORK")
endif()
endif()
set(install_prefix_option "")
get_cmake_property(install_prefix _qt_internal_sbom_install_prefix)
if(install_prefix)
set(install_prefix_option INSTALL_PREFIX "${install_prefix}")
endif()
_qt_internal_forward_function_args(