forked from p4lang/p4c
-
Notifications
You must be signed in to change notification settings - Fork 1
/
P4CUtils.cmake
422 lines (388 loc) · 16 KB
/
P4CUtils.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
include (CheckCCompilerFlag)
include (CheckCXXCompilerFlag)
# Some color definitions. Courtesy of https://stackoverflow.com/a/19578320
string(ASCII 27 ESC)
set(COLOURRESET "${ESC}[m")
set(COLOURBOLD "${ESC}[1m")
set(RED "${ESC}[31m")
set(GREEN "${ESC}[32m")
set(YELLOW "${ESC}[33m")
set(BLUE "${ESC}[34m")
set(MAGENTA "${ESC}[35m")
set(CYAN "${ESC}[36m")
set(WHITE "${ESC}[37m")
set(BOLDRED "${ESC}[1;31m")
set(BOLDGREEN "${ESC}[1;32m")
set(BOLDYELLOW "${ESC}[1;33m")
set(BOLDBLUE "${ESC}[1;34m")
set(BOLDMAGENTA "${ESC}[1;35m")
set(BOLDCYAN "${ESC}[1;36m")
set(BOLDWHITE "${ESC}[1;37m")
# test and add a C++ compiler option if supported
MACRO (add_cxx_compiler_option option)
string (REPLACE "+" "P" escaped_option1 ${option})
string (REPLACE "-" "" escaped_option ${escaped_option1})
set (_success "_HAVE_OPTION_${escaped_option}_")
set (__required_flags_backup "${CMAKE_REQUIRED_FLAGS}")
set (CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${${PROJECT_NAME}_CXX_FLAGS}")
check_cxx_compiler_flag (${option} ${_success})
set (CMAKE_REQUIRED_FLAGS ${__required_flags_backup})
if (${_success})
set (${PROJECT_NAME}_CXX_FLAGS "${${PROJECT_NAME}_CXX_FLAGS} ${option}")
endif (${_success})
endmacro (add_cxx_compiler_option)
include (CheckCXXSourceCompiles)
# check if a compiler supports a certain feature by compiling the program
MACRO (check_compiler_feature option program var)
set (__required_flags_backup "${CMAKE_REQUIRED_FLAGS}")
set (CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${option}")
check_cxx_source_compiles ("${program}" ${var})
IF (${var} AND NOT "${option}" STREQUAL "")
set (P4C_CXX_FLAGS "${P4C_CXX_FLAGS} ${option}")
ENDIF ()
if (${var})
add_definitions (-D${var})
endif ()
set (CMAKE_REQUIRED_FLAGS ${__required_flags_backup})
endmacro (check_compiler_feature)
# add a required library
include (CheckLibraryExists)
# checks if a library exists and adds it to the list of p4c dependencies
# supports an additional argument: REQUIRED. If present, it will throw
# a fatal error if the library does not exist.
macro (p4c_add_library name symbol var)
check_library_exists (${name} ${symbol} "" ${var})
if (${var})
set (P4C_LIB_DEPS "${P4C_LIB_DEPS};${name}")
else()
if("${ARGN}" STREQUAL "REQUIRED")
MESSAGE (FATAL_ERROR "Can not find required library: ${name}")
endif()
endif()
endmacro(p4c_add_library)
# Utility function which adds @param filelist to a global list of ${label}-files
function(add_files dir filelist label)
if (NOT filelist)
message(WARNING "Input file list is empty. Returning.")
return()
endif()
# Initialize an empty list.
set (__FileList "")
foreach(__f ${filelist})
string(REGEX MATCH "^/.*" abs_path "${__f}")
if (NOT ${abs_path} EQUAL "")
list (APPEND __FileList "${__f}")
else()
list (APPEND __FileList "${dir}/${__f}")
endif()
endforeach(__f)
# Get the global label property and append to it.
get_property(${label}_FILES GLOBAL PROPERTY ${label}-files)
list (APPEND ${label}_FILES "${__FileList}")
list(REMOVE_DUPLICATES ${label}_FILES)
set_property(GLOBAL PROPERTY ${label}-files "${${label}_FILES}")
endfunction(add_files)
# Add files with the appropriate path to the list of cpplint-linted files.
function(add_cpplint_files dir filelist)
add_files(${dir} "${filelist}" CPPLINT)
endfunction(add_cpplint_files)
# Add files with the appropriate path to the list of clang-format-linted files.
function(add_clang_format_files dir filelist)
add_files(${dir} "${filelist}" CLANG_FORMAT)
endfunction(add_clang_format_files)
# Add files with the appropriate path to the list of clang-tidy-linted files.
function(add_clang_tidy_files dir filelist)
add_files(${dir} "${filelist}" CLANG_TIDY)
endfunction(add_clang_tidy_files)
# Add files with the appropriate path to the list of black-linted files.
function(add_black_files dir filelist)
add_files(${dir} "${filelist}" BLACK)
endfunction(add_black_files)
macro(p4c_test_set_name name tag alias)
set(${name} ${tag}/${alias})
endmacro(p4c_test_set_name)
function(append value)
foreach(variable ${ARGN})
set(${variable} "${${variable}} ${value}" PARENT_SCOPE)
endforeach(variable)
endfunction()
# add a single test to the testsuite
# Arguments:
# - tag is a label for the set of test suite where this test belongs
# (for example, p4, p14_to_16, bmv2, ebpf
# - driver is the script that is used to run the test and compare the results
# - isXfail is boolean that specifies whether this test is expected to fail
# - alias is a possibly different name for the test such that the
# same p4 program can be used in different test configurations.
# Must be unique across the test suite.
# - p4test is the name of the p4 program to test (path relative to the p4c directory)
# - args is a list of arguments to pass to the test
#
# It generates a ${p4name}.test file invoking ${driver} on the p4
# program with command line arguments ${args}
# Sets the timeout on tests at 300s (for the slow Travis machines)
#
macro(p4c_add_test_with_args tag driver isXfail alias p4test test_args cmake_args)
set(__testfile "${P4C_BINARY_DIR}/${tag}/${p4test}.test")
file (WRITE ${__testfile} "#! /usr/bin/env bash\n")
file (APPEND ${__testfile} "# Generated file, modify with care\n\n")
file (APPEND ${__testfile} "cd ${P4C_BINARY_DIR}\n")
file (APPEND ${__testfile} "${driver} ${P4C_SOURCE_DIR} ${test_args} \"$@\" ${P4C_SOURCE_DIR}/${p4test}")
execute_process(COMMAND chmod +x ${__testfile})
p4c_test_set_name(__testname ${tag} ${alias})
separate_arguments(__args UNIX_COMMAND ${cmake_args})
add_test (NAME ${__testname}
COMMAND ${tag}/${p4test}.test ${__args}
WORKING_DIRECTORY ${P4C_BINARY_DIR})
if (NOT DEFINED ${tag}_timeout)
set (${tag}_timeout 300)
endif()
if (${isXfail})
set_tests_properties(${__testname} PROPERTIES LABELS "${tag};XFAIL" TIMEOUT ${${tag}_timeout} WILL_FAIL 1)
else()
set_tests_properties(${__testname} PROPERTIES LABELS ${tag} TIMEOUT ${${tag}_timeout})
endif()
endmacro(p4c_add_test_with_args)
macro(p4c_add_test_label tag newLabel testname)
set (__testname ${tag}/${testname})
get_property(__labels TEST ${__testname} PROPERTY LABELS)
set_tests_properties(${__testname} PROPERTIES LABELS "${__labels};${newLabel}")
endmacro(p4c_add_test_label)
# generate all the tests specified in the tests list
# Arguments:
# - tag is a label for the set of tests, for example, p4, p14_to_16, bmv2, ebpf
# - driver is the script that is used to run the tests and compare the results
# - testsuite is a list of test names
# - xfail is a set of tests that are expected to fail
#
# The macro generates the test files in a directory prefixed by tag.
#
macro(p4c_add_test_list tag driver tests xfail)
set (__xfail_list "${xfail}")
set (__test_list "${tests}")
set (__testCounter 0)
set (__xfailCounter 0)
list (LENGTH __test_list __nTests)
foreach(t ${__test_list})
list (FIND __xfail_list ${t} __xfail_test)
if(__xfail_test GREATER -1)
p4c_add_test_with_args (${tag} ${driver} TRUE ${t} ${t} "${ARGN}" "")
math (EXPR __xfailCounter "${__xfailCounter} + 1")
else()
p4c_add_test_with_args (${tag} ${driver} FALSE ${t} ${t} "${ARGN}" "")
endif() # __xfail_test
endforeach() # tests
math (EXPR __testCounter "${__testCounter} + ${__nTests}")
# add the tag to the list
set (TEST_TAGS ${TEST_TAGS} ${tag} CACHE INTERNAL "test tags")
MESSAGE(STATUS "Added ${__testCounter} tests to '${tag}' (${__xfailCounter} xfails)")
endmacro(p4c_add_test_list)
# generate a list of test name suffixes based on specified testsuites
function(p4c_find_test_names testsuites tests)
set(__tests "")
p4c_sanitize_path("${testsuites}" abs_paths)
foreach(ts ${abs_paths})
file (GLOB __testfiles RELATIVE ${P4C_SOURCE_DIR} ${ts})
list (APPEND __tests ${__testfiles})
endforeach()
set(${tests} "${__tests}" PARENT_SCOPE)
endfunction()
# convert the paths from a list of input files to their absolute paths
# does not follow symlinks.
# - files is a list of (relative) file paths
# - abs_files is the return set of absolute file paths
function(p4c_sanitize_path files abs_files)
foreach(file ${files})
get_filename_component(__file "${file}"
ABSOLUTE BASE_DIR "${P4C_SOURCE_DIR}")
list (APPEND __abs_files ${__file})
endforeach()
set(${abs_files} "${__abs_files}" PARENT_SCOPE)
endfunction()
# generate all the tests specified in the testsuites: builds a list of tests
# from the testsuite patterns by calling p4c_find_test_names then pass the list
# to p4c_add_test_list
# Arguments:
# - tag is a label for the set of tests, for example, p4, p14_to_16, bmv2, ebpf
# - driver is the script that is used to run the tests and compare the results
# - testsuite is a list of directory patterns, e.g.: testdata/p4_16_samples/*.p4
# - xfail is a set of tests that are expected to fail
#
# The macro generates the test files in a directory prefixed by tag.
#
macro(p4c_add_tests tag driver testsuites xfails)
set(__tests "")
set(__xfails "")
p4c_find_test_names("${testsuites}" __tests)
p4c_find_test_names("${xfails}" __xfails)
p4c_add_test_list (${tag} ${driver} "${__tests}" "${__xfails}" "${ARGN}")
endmacro(p4c_add_tests)
# same as p4c_add_tests but adds --p4runtime flag when invoking test driver
# unless test is listed in p4rt_exclude
macro(p4c_add_tests_w_p4runtime tag driver testsuites xfails p4rt_exclude)
set(__tests "")
set(__xfails "")
p4c_find_test_names("${testsuites}" __tests)
p4c_find_test_names("${xfails}" __xfails)
set(__tests_no_p4runtime "${p4rt_exclude}")
set(__tests_p4runtime "${__tests}")
list (REMOVE_ITEM __tests_p4runtime ${__tests_no_p4runtime})
p4c_add_test_list (${tag} ${driver} "${__tests_no_p4runtime}" "${__xfails}" "${ARGN}")
p4c_add_test_list (${tag} ${driver} "${__tests_p4runtime}" "${__xfails}" "--p4runtime ${ARGN}")
endmacro(p4c_add_tests_w_p4runtime)
# add rules to make check and recheck for a specific test suite
macro (p4c_add_make_check tag)
if ( "${tag}" STREQUAL "all")
set (__tests_regex "'.*'")
else()
# avoid escaping spaces
set (__tests_regex "'${tag}/.*'")
endif()
get_filename_component (__logDir ${P4C_XFAIL_LOG} DIRECTORY)
add_custom_target(check-${tag}
# list the xfail tests
COMMAND ${CMAKE_COMMAND} -E make_directory ${__logDir}
COMMAND ${CMAKE_COMMAND} -E echo "XFAIL tests:" > ${P4C_XFAIL_LOG} &&
${CMAKE_CTEST_COMMAND} --show-only -L XFAIL --tests-regex "${__tests_regex}" >> ${P4C_XFAIL_LOG}
COMMAND ${CMAKE_CTEST_COMMAND} ${P4C_TEST_FLAGS} --tests-regex "${__tests_regex}"
COMMENT "Running tests for tag ${tag}")
add_custom_target(recheck-${tag}
# list the xfail tests
COMMAND ${CMAKE_COMMAND} -E make_directory ${__logDir}
COMMAND ${CMAKE_COMMAND} -E echo "XFAIL tests:" > ${P4C_XFAIL_LOG} &&
${CMAKE_CTEST_COMMAND} --show-only -L XFAIL --tests-regex "${__tests_regex}" >> ${P4C_XFAIL_LOG}
COMMAND ${CMAKE_CTEST_COMMAND} ${P4C_TEST_FLAGS} --tests-regex "${__tests_regex}" --rerun-failed
COMMENT "Re-running failed tests for tag ${tag}")
endmacro(p4c_add_make_check)
# check for the number of cores on the machine and return them in ${var}
macro (p4c_get_nprocs var)
if(APPLE)
execute_process (COMMAND sysctl -n hw.logicalcpu
OUTPUT_VARIABLE ${var}
OUTPUT_STRIP_TRAILING_WHITESPACE
RESULT_VARIABLE rc)
else()
execute_process (COMMAND nproc
OUTPUT_VARIABLE ${var}
OUTPUT_STRIP_TRAILING_WHITESPACE
RESULT_VARIABLE rc)
endif()
# MESSAGE ("p4c_get_nprocs: ${rc} ${${var}}")
if (NOT ${rc} EQUAL 0)
set (${var} 4)
endif()
endmacro (p4c_get_nprocs)
# poor's cmake replacement for $(shell grep -L xxx $(shell -l yyy))
# It takes as arguments:
# - input_files: a pattern for files (full path)
# - test_list: a variable to be set to the resulting list of tests matching
# - pairs of:
# - pattern selector: INCLUDE or EXCLUDE
# - pattern list: a list of patterns that should be included or excluded
# Returns:
# - the list of tests matching search_patterns and not exclude_patterns
function(p4c_find_tests input_files test_list incl_excl patterns)
if (${ARGC} EQUAL 4)
if (${ARGV2} STREQUAL INCLUDE)
set(search_patterns "${ARGV3}")
elseif (${ARGV2} STREQUAL EXCLUDE)
set(exclude_patterns "${ARGV3}")
else()
MESSAGE (FATAL_ERROR "Invalid pattern selector ${ARGV2}")
endif()
elseif(${ARGC} EQUAL 6)
if (${ARGV2} STREQUAL INCLUDE AND ${ARGV4} STREQUAL EXCLUDE)
set(search_patterns "${ARGV3}")
set(exclude_patterns "${ARGV5}")
elseif (${ARGV4} STREQUAL INCLUDE AND ${ARGV2} STREQUAL EXCLUDE)
set(search_patterns "${ARGV5}")
set(exclude_patterns "${ARGV3}")
else()
MESSAGE (FATAL_ERROR "Invalid pattern selector combo: ${ARGV2}/${ARGV4}")
endif()
else()
MESSAGE(FATAL_ERROR "Invalid number of arguments ${ARGC} for find_tests")
endif()
if (DEFINED search_patterns)
list (LENGTH search_patterns __pLen)
# MESSAGE ("include pattern: ${search_patterns}, length ${__pLen}")
else()
# only exclude patterns
set(__pLen 0)
endif()
# if (DEFINED exclude_patterns)
# MESSAGE ("exclude pattern: ${exclude_patterns}, length ${__pLen}")
# endif()
set (inputList "")
foreach (l ${input_files})
file (GLOB __inputList ${l})
list (APPEND inputList ${__inputList})
endforeach()
list (REMOVE_DUPLICATES inputList)
foreach (f IN LISTS inputList)
set (__found 0)
foreach(p ${search_patterns})
file (STRINGS ${f} __found_strings REGEX ${p})
list (LENGTH __found_strings __found_len)
if (__found_len GREATER 0)
math (EXPR __found "${__found} + 1")
endif()
endforeach() # search_patterns
foreach(p ${exclude_patterns})
file (STRINGS ${f} __found_strings REGEX ${p})
list (LENGTH __found_strings __found_len)
if (__found_len GREATER 0)
set (__found -1)
endif()
endforeach() # exclude_patterns
if (__found EQUAL __pLen)
list (APPEND __p4tests ${f})
endif()
endforeach() # test files
# return
set(${test_list} ${__p4tests} PARENT_SCOPE)
endfunction(p4c_find_tests)
# if we have a reason for failure, then use that regular expression to
# make the test succeed. If that changes, we know the test moved to a
# different failure. Also turn off automatic ignoring of failures (WILL_FAIL).
macro(p4c_add_xfail_reason tag reason)
set (__tests "${ARGN}")
string (TOUPPER ${tag} __upperTag)
foreach (test IN LISTS __tests)
list (FIND ${__upperTag}_MUST_PASS_TESTS ${test} __isMustPass)
if (${__isMustPass} EQUAL -1) # not a mandatory pass test
p4c_test_set_name(__testname ${tag} ${test})
if ( "${reason}" STREQUAL "")
set_tests_properties(${__testname} PROPERTIES WILL_FAIL 1)
else ()
set_tests_properties(${__testname} PROPERTIES
PASS_REGULAR_EXPRESSION ${reason}
WILL_FAIL 0)
endif()
p4c_add_test_label(${tag} "XFAIL" ${test})
else()
message(WARNING "${test} can not be listed as an xfail. It must always pass!")
endif()
endforeach()
endmacro(p4c_add_xfail_reason)
# Effectively emulates fetchcontent_makeavailable but does not add the module to install.
macro(fetchcontent_makeavailable_but_exclude_install content)
FetchContent_GetProperties(${content})
if(NOT ${content}_POPULATED)
FetchContent_Populate(${content})
add_subdirectory(${${content}_SOURCE_DIR} ${${content}_BINARY_DIR} EXCLUDE_FROM_ALL)
endif()
endmacro(fetchcontent_makeavailable_but_exclude_install)
# Collect all currently added targets in all subdirectories
#
# Parameters: - _result the list containing all found targets - _dir root directory to start
# looking from
# Sourced from https://stackoverflow.com/a/60232044
function(get_all_targets _result _dir)
get_property(_subdirs DIRECTORY "${_dir}" PROPERTY SUBDIRECTORIES)
foreach(_subdir IN LISTS _subdirs)
get_all_targets(${_result} "${_subdir}")
endforeach()
get_directory_property(_sub_targets DIRECTORY "${_dir}" BUILDSYSTEM_TARGETS)
set(${_result} ${${_result}} ${_sub_targets} PARENT_SCOPE)
endfunction()