1# SPDX-License-Identifier: BSD-3-Clause
2
3# Generates header for which version is taken from (in order of precedence):
4# 	1) .tarball-version file
5#	2) git
6#
7# Version is checked during configuration step and for every target
8# that has check_version_h target as dependency
9
10cmake_minimum_required(VERSION 3.10)
11
12set(VERSION_CMAKE_PATH ${CMAKE_CURRENT_LIST_DIR}/version.cmake)
13
14
15# In an ideal world, every CI engine records the most basic and most
16# important information:
17# - current date and time
18# - git version of the pull request
19# - git version of the moving branch it's being merged with
20#
21# In the real world, some CI results use a random timezone without
22# telling which one or don't provide any time at all.
23string(TIMESTAMP build_start_time UTC)
24message(STATUS "version.cmake starting SOF build at ${build_start_time} UTC")
25
26# Most CI engines test a temporary merge of the pull request with a
27# moving target: the latest target branch. In that case the SHA version
28# gathered by git describe is disposable hence useless. Only the
29# --parents SHA are useful.
30message(STATUS "Building git commit with parent(s):")
31# Note execute_process() failures are ignored by default (missing git...)
32execute_process(
33	COMMAND git log --parents --oneline --decorate -n 1 HEAD
34	)
35
36
37# Don't confuse this manual _input_ file with the other, output file of
38# the same name auto-generated in the top _build_ directory by "make
39# dist", see dist.cmake
40set(TARBALL_VERSION_FILE_NAME ".tarball-version")
41set(TARBALL_VERSION_SOURCE_PATH "${SOF_ROOT_SOURCE_DIRECTORY}/${TARBALL_VERSION_FILE_NAME}")
42
43if(EXISTS ${TARBALL_VERSION_SOURCE_PATH})
44	file(STRINGS ${TARBALL_VERSION_SOURCE_PATH} lines ENCODING "UTF-8")
45	list(GET lines 0 GIT_TAG)
46	list(GET lines 1 GIT_LOG_HASH)
47	message(STATUS "Found ${TARBALL_VERSION_FILE_NAME}")
48else()
49	# execute_process() errors are not fatal by default!
50	execute_process(
51	        COMMAND git describe --tags --abbrev=12 --match v* --dirty
52		WORKING_DIRECTORY ${SOF_ROOT_SOURCE_DIRECTORY}
53		OUTPUT_VARIABLE GIT_TAG
54		OUTPUT_STRIP_TRAILING_WHITESPACE
55	)
56
57	execute_process(COMMAND git log --pretty=format:%h -1
58		WORKING_DIRECTORY ${SOF_ROOT_SOURCE_DIRECTORY}
59		OUTPUT_VARIABLE GIT_LOG_HASH
60		OUTPUT_STRIP_TRAILING_WHITESPACE
61	)
62endif()
63
64if(NOT GIT_TAG MATCHES "^v")
65	message(WARNING
66	  "git describe found ${GIT_TAG} / nothing starting with 'v'. Shallow clone?")
67	set(GIT_TAG "v0.0-0-g0000")
68endif()
69
70message(STATUS "GIT_TAG / GIT_LOG_HASH : ${GIT_TAG} / ${GIT_LOG_HASH}")
71
72string(REGEX MATCH "^v([0-9]+)[.]([0-9]+)([.]([0-9]+))?" ignored "${GIT_TAG}")
73set(SOF_MAJOR ${CMAKE_MATCH_1})
74set(SOF_MINOR ${CMAKE_MATCH_2})
75set(SOF_MICRO ${CMAKE_MATCH_4})
76
77if(NOT SOF_MICRO MATCHES "^[0-9]+$")
78	set(SOF_MICRO 0)
79endif()
80
81string(SUBSTRING "${GIT_LOG_HASH}" 0 5 SOF_TAG)
82if(NOT SOF_TAG)
83	set(SOF_TAG 0)
84endif()
85
86# Calculate source hash value, used to check ldc file and firmware compatibility
87if(EXISTS ${SOF_ROOT_SOURCE_DIRECTORY}/.git/)
88	set(SOURCE_HASH_DIR "${SOF_ROOT_BINARY_DIRECTORY}/source_hash")
89	file(MAKE_DIRECTORY ${SOURCE_HASH_DIR})
90	# list tracked files from src directory
91	execute_process(COMMAND git ls-files src
92			WORKING_DIRECTORY ${SOF_ROOT_SOURCE_DIRECTORY}
93			OUTPUT_FILE "${SOURCE_HASH_DIR}/tracked_file_list"
94		)
95	# calculate hash of each listed files (from file version saved in file system)
96	execute_process(COMMAND git hash-object --stdin-paths
97			WORKING_DIRECTORY ${SOF_ROOT_SOURCE_DIRECTORY}
98			INPUT_FILE "${SOURCE_HASH_DIR}/tracked_file_list"
99			OUTPUT_FILE "${SOURCE_HASH_DIR}/tracked_file_hash_list"
100		)
101	# then calculate single hash of previously calculated hash list
102	execute_process(COMMAND git hash-object --stdin
103			WORKING_DIRECTORY ${SOF_ROOT_SOURCE_DIRECTORY}
104			OUTPUT_STRIP_TRAILING_WHITESPACE
105			INPUT_FILE "${SOURCE_HASH_DIR}/tracked_file_hash_list"
106			OUTPUT_VARIABLE SOF_SRC_HASH_LONG
107		)
108	string(SUBSTRING ${SOF_SRC_HASH_LONG} 0 8 SOF_SRC_HASH)
109	message(STATUS "Source content hash: ${SOF_SRC_HASH}")
110else()
111	if("${GIT_LOG_HASH}")
112		string(SUBSTRING "${GIT_LOG_HASH}" 0 8 SOF_SRC_HASH)
113	else()
114		set(SOF_SRC_HASH "0")
115	endif()
116	message(WARNING "Source content hash not computed without git, using GIT_LOG_HASH instead")
117endif()
118
119# for SOF_BUILD
120include(${CMAKE_CURRENT_LIST_DIR}/version-build-counter.cmake)
121
122function(sof_check_version_h)
123	string(CONCAT header_content
124		"#define SOF_MAJOR ${SOF_MAJOR}\n"
125		"#define SOF_MINOR ${SOF_MINOR}\n"
126		"#define SOF_MICRO ${SOF_MICRO}\n"
127		"#define SOF_TAG \"${SOF_TAG}\"\n"
128		"#define SOF_BUILD ${SOF_BUILD}\n"
129		"#define SOF_GIT_TAG \"${GIT_TAG}\"\n"
130		"#define SOF_SRC_HASH 0x${SOF_SRC_HASH}\n"
131	)
132
133	if(EXISTS "${VERSION_H_PATH}")
134		file(READ "${VERSION_H_PATH}" old_version_content)
135		if("${header_content}" STREQUAL "${old_version_content}")
136			message(STATUS "Up-to-date ${VERSION_H_PATH}")
137			return()
138		endif()
139	endif()
140
141	message(STATUS "Generating ${VERSION_H_PATH}")
142	file(WRITE "${VERSION_H_PATH}" "${header_content}")
143endfunction()
144
145# Run these only if not run as script
146if("${CMAKE_SCRIPT_MODE_FILE}" STREQUAL "")
147	add_custom_target(
148		check_version_h
149		BYPRODUCTS ${VERSION_H_PATH}
150		COMMAND ${CMAKE_COMMAND}
151			-DVERSION_H_PATH=${VERSION_H_PATH}
152			-DSOF_ROOT_SOURCE_DIRECTORY=${SOF_ROOT_SOURCE_DIRECTORY}
153			-DSOF_ROOT_BINARY_DIRECTORY=${SOF_ROOT_BINARY_DIRECTORY}
154			-P ${VERSION_CMAKE_PATH}
155		COMMENT "Checking ${VERSION_H_PATH}"
156		VERBATIM
157		USES_TERMINAL
158	)
159endif()
160
161sof_check_version_h()
162