• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1
2# Reverses a space-separated list of words.
3reverse = $(if $(1),$(call reverse,$(wordlist 2,$(words $(1)),$(1)))) $(firstword $(1))
4
5# Get macros only (i.e. the ones starting with -D) from two lists and remove duplicates
6getmacros = $(patsubst -D%,%,$(filter -D%,$(sort $(filter -D%, $(1)) $(filter -D%, $(2)))))
7
8# Look for platform or target-specific implementation files to replace reference
9# implementations with, given a tag. These are expected to occur in subfolders
10# of a directory where a reference implementation exists, and have the same
11# interface and header file. For example,
12# tensorflow/lite/micro/examples/micro_speech/audio_provider.cc
13# defines a module for supplying audio data, but since no platform or OS can be
14# presumed, it just always returns zeroes for its samples. The MacOS-specific
15# tensorflow/lite/micro/examples/micro_speech/osx/audio_provider.cc
16# has an implementation that relies on CoreAudio, and there are equivalent
17# versions for other operating systems.
18# The specific implementation yielded by the first tag in the list that produces
19# a match is returned, else the reference version if none of the tags produce a
20# match.
21# All lists of source files are put through this substitution process with the
22# tags of their target OS and architecture, so that implementations can be added
23# by simply placing them in the file tree, with no changes to the build files
24# needed.
25# One confusing thing about this implementation is that we're using wildcard to
26# act as a 'does file exist?' function, rather than expanding an expression.
27# Wildcard will return an empty string if given a plain file path with no actual
28# wildcards, if the file doesn't exist, so taking the first word of the list
29# between that and the reference path will pick the specialized one if it's
30# available.
31# Another fix is that originally if neither file existed(either the original or
32# a specialized version) this would return an empty string.Because this is
33# sometimes called on third party library files before they've been downloaded,
34# this caused mysterious errors, so an initial if conditional was added so that
35# specializations are only looked for if the original file exists.
36substitute_specialized_implementation = \
37  $(if $(wildcard $(1)),$(firstword $(wildcard $(dir $(1))$(2)/$(notdir $(1))) $(wildcard $(1))),$(1))
38substitute_specialized_implementations = \
39  $(foreach source,$(1),$(call substitute_specialized_implementation,$(source),$(2)))
40# Here we're first looking for specialized implementations in ref_dir/$(TAG1)
41# and then ref_dir/$(TAG2), etc, before falling back to ref_dir's
42# implementation.
43# The argument to this function should be a list of space-separated file paths,
44# with any wildcards already expanded.
45define specialize_on_tags
46$(if $(2),$(call substitute_specialized_implementations,$(call specialize_on_tags,$(1),$(wordlist 2,$(words $(2)),$(2))),$(firstword $(2))),$(1))
47endef
48# The entry point that most targets should use to find implementation-specific
49# versions of their source files. The only argument is a list of file paths.
50specialize = $(call specialize_on_tags,$(1),$(strip $(call reverse,$(ALL_TAGS))))
51
52# TODO(b/143904317): It would be better to have the dependency be
53# THIRD_PARTY_TARGETS instead of third_party_downloads. However, that does not
54# quite work for the generate_project functions.
55#
56# Creates a set of rules to build a standalone makefile project for an
57# executable, including all of the source and header files required in a
58# separate folder and a simple makefile.
59# Arguments are:
60# 1 - Project type (make, mbed, etc).
61# 2 - Project file template name.
62# 3 - Name of executable.
63# 4 - List of C/C++ source files needed to build the target.
64# 5 - List of C/C++ header files needed to build the target.
65# 6 - Linker flags required.
66# 7 - C++ compilation flags needed.
67# 8 - C compilation flags needed.
68# Calling eval on the output will create a <Name>_makefile target that you
69# can invoke to create the standalone project.
70define generate_project
71$(PRJDIR)$(3)/$(1)/%: % third_party_downloads
72	@mkdir -p $$(dir $$@)
73	@cp $$< $$@
74
75$(PRJDIR)$(3)/$(1)/third_party/%: tensorflow/lite/micro/tools/make/downloads/% third_party_downloads
76	@mkdir -p $$(dir $$@)
77	@cp $$< $$@
78
79$(PRJDIR)$(3)/$(1)/%: tensorflow/lite/micro/tools/make/templates/%.tpl
80	@mkdir -p $$(dir $$@)
81	@sed -E 's#\%\{SRCS\}\%#$(4)#g' $$< | \
82	sed -E 's#\%\{EXECUTABLE\}\%#$(3)#g' | \
83	sed -E 's#\%\{LINKER_FLAGS\}\%#$(6)#g' | \
84	sed -E 's#\%\{CXX_FLAGS\}\%#$(7)#g' | \
85	sed -E 's#\%\{CC_FLAGS\}\%#$(8)#g' > $$@
86
87$(PRJDIR)$(3)/$(1)/keil_project.uvprojx: tensorflow/lite/micro/tools/make/templates/keil_project.uvprojx.tpl
88	@mkdir -p $$(dir $$@)
89	@python tensorflow/lite/micro/tools/make/generate_keil_project.py \
90        --input_template=$$< --output_file=$$@ --executable=$(3) \
91        --srcs="$(4)" --hdrs="$(5)" --include_paths="$$(PROJECT_INCLUDES)"
92
93$(PRJDIR)$(3)/$(1)/.vscode/tasks.json : tensorflow/lite/micro/tools/make/templates/tasks.json.$(1).tpl
94	@mkdir -p $$(dir $$@)
95	@cp $$< $$@
96
97generate_$(3)_$(1)_project: $(addprefix $(PRJDIR)$(3)/$(1)/, $(4) $(5) $(2))
98ifeq (mbed, $(1))
99	$(eval macrolist := $(call getmacros, $7, $8))
100	$(eval jsonfilename := $(PRJDIR)$(3)/$(1)/mbed_app)
101	@awk 'FNR==NR{ if (/}/) p=NR; next} 1; FNR==(p-1){ n=split("$(macrolist)",a," "); print("    ,\"macros\": [");for (i=1; i <= n; i++){ printf("        \"%s\"", a[i]); if(i<n){printf(",\n")}}printf("\n    ]\n")}' \
102        $(jsonfilename).json $(jsonfilename).json > $(jsonfilename).tmp && mv $(jsonfilename).tmp $(jsonfilename).json
103endif
104
105list_$(3)_$(1)_files:
106	@echo $(4) $(5)
107
108ALL_PROJECT_TARGETS += generate_$(3)_$(1)_project
109endef
110
111# Creates a set of rules to build a standalone makefile project for the ARC platform
112# including all of the source and header files required in a
113# separate folder and a simple makefile.
114# Arguments are:
115# 1 - Project type (make, mbed, etc).
116# 2 - Project file template name.
117# 3 - Name of executable.
118# 4 - List of C/C++ source files needed to build the target.
119# 5 - List of C/C++ header files needed to build the target.
120# 6 - Linker flags required.
121# 7 - C++ compilation flags needed.
122# 8 - C compilation flags needed.
123# Calling eval on the output will create a <Name>_makefile target that you
124# can invoke to create the standalone project.
125define generate_arc_project
126
127ifeq ($(TARGET_ARCH), arc)
128$(PRJDIR)$(3)/$(1)/Makefile: tensorflow/lite/micro/tools/make/templates/Makefile.tpl
129	@mkdir -p $$(dir $$@)
130	@sed -E 's#\%\{SRCS\}\%#$(4)#g' $$< | \
131	sed -E '1 i\CC = ccac\nCXX = ccac\nLD = ccac\n' | \
132	sed -E 's#\%\{EXECUTABLE\}\%#$(3).elf#g' | \
133	sed -E 's#\%\{LINKER_FLAGS\}\%#$(6)#g' | \
134	sed -E 's#\%\{CXX_FLAGS\}\%#$(7)#g' | \
135	sed -E 's#\%\{CC_FLAGS\}\%#$(8)#g' > $$@
136
137# Special rule to copy TCF in case the local filesystem file name has been defined
138ifneq ($(TCF_FILE_NAME), )
139$(PRJDIR)$(3)/$(1)/$(TCF_FILE_NAME): $(TCF_FILE)
140	@cp $$< $$@
141endif
142endif
143endef
144
145# Creates a set of rules to build a standalone Arduino project for an
146# executable, including all of the source and header files required in a
147# separate folder and a simple makefile.
148# Arguments are:
149# 1 - Project file template names.
150# 2 - Name of executable.
151# 3 - List of C/C++ source files needed to build the target.
152# 4 - List of C/C++ header files needed to build the target.
153# 5 - Linker flags required.
154# 6 - C++ compilation flags needed.
155# 7 - C compilation flags needed.
156# Calling eval on the output will create a <Name>_makefile target that you
157# can invoke to create the standalone project.
158define generate_arduino_project
159
160$(PRJDIR)$(2)/arduino/examples/%.cpp: tensorflow/lite/micro/examples/%.cc
161	@mkdir -p $$(dir $$@)
162	@python tensorflow/lite/micro/tools/make/transform_source.py \
163        --platform=arduino \
164        --is_example_source \
165        --source_path="$$<" \
166        --third_party_headers="$(4)" < $$< > $$@
167
168$(PRJDIR)$(2)/arduino/examples/%.h: tensorflow/lite/micro/examples/%.h
169	@mkdir -p $$(dir $$@)
170	@python tensorflow/lite/micro/tools/make/transform_source.py \
171        --platform=arduino \
172        --is_example_source \
173        --source_path="$$<" \
174        --third_party_headers="$(4)" < $$< > $$@
175
176$(PRJDIR)$(2)/arduino/examples/%/main.ino: tensorflow/lite/micro/examples/%/main_functions.cc
177	@mkdir -p $$(dir $$@)
178	@python tensorflow/lite/micro/tools/make/transform_source.py \
179        --platform=arduino \
180        --is_example_ino \
181        --source_path="$$<" \
182        --third_party_headers="$(4)" < $$< > $$@
183
184$(PRJDIR)$(2)/arduino/src/%.cpp: %.cc
185	@mkdir -p $$(dir $$@)
186	@python tensorflow/lite/micro/tools/make/transform_source.py \
187        --platform=arduino \
188        --third_party_headers="$(4)" < $$< > $$@
189
190$(PRJDIR)$(2)/arduino/src/%.h: %.h third_party_downloads
191	@mkdir -p $$(dir $$@)
192	@python tensorflow/lite/micro/tools/make/transform_source.py \
193        --platform=arduino \
194        --third_party_headers="$(4)" < $$< > $$@
195
196$(PRJDIR)$(2)/arduino/LICENSE: LICENSE
197	@mkdir -p $$(dir $$@)
198	@cp $$< $$@
199
200$(PRJDIR)$(2)/arduino/src/%: % third_party_downloads
201	@mkdir -p $$(dir $$@)
202	@python tensorflow/lite/micro/tools/make/transform_source.py \
203        --platform=arduino \
204        --third_party_headers="$(4)" < $$< > $$@
205
206$(PRJDIR)$(2)/arduino/src/third_party/%: tensorflow/lite/micro/tools/make/downloads/% third_party_downloads
207	@mkdir -p $$(dir $$@)
208	@python tensorflow/lite/micro/tools/make/transform_source.py \
209        --platform=arduino \
210        --third_party_headers="$(4)" < $$< > $$@
211
212$(PRJDIR)$(2)/arduino/src/third_party/%.cpp: tensorflow/lite/micro/tools/make/downloads/%.cc third_party_downloads
213	@mkdir -p $$(dir $$@)
214	@python tensorflow/lite/micro/tools/make/transform_source.py \
215        --platform=arduino \
216        --third_party_headers="$(4)" < $$< > $$@
217
218$(PRJDIR)$(2)/arduino/src/third_party/flatbuffers/include/flatbuffers/base.h: tensorflow/lite/micro/tools/make/downloads/flatbuffers/include/flatbuffers/base.h third_party_downloads
219	@mkdir -p $$(dir $$@)
220	@python tensorflow/lite/micro/tools/make/transform_source.py \
221        --platform=arduino \
222        --third_party_headers="$(4)" < $$< | \
223        sed -E 's/utility\.h/utility/g' > $$@
224
225$(PRJDIR)$(2)/arduino/src/third_party/kissfft/kiss_fft.h: tensorflow/lite/micro/tools/make/downloads/kissfft/kiss_fft.h third_party_downloads
226	@mkdir -p $$(dir $$@)
227	@python tensorflow/lite/micro/tools/make/transform_source.py \
228        --platform=arduino \
229        --third_party_headers="$(4)" < $$< | \
230        sed -E 's@#include <string.h>@//#include <string.h> /* Patched by helper_functions.inc for Arduino compatibility */@g' > $$@
231
232$(PRJDIR)$(2)/arduino/%: tensorflow/lite/micro/tools/make/templates/%
233	@mkdir -p $$(dir $$@)
234	@sed -E 's#\%\{SRCS\}\%#$(3)#g' $$< | \
235	sed -E 's#\%\{EXECUTABLE\}\%#$(2)#g' | \
236	sed -E 's#\%\{LINKER_FLAGS\}\%#$(5)#g' | \
237	sed -E 's#\%\{CXX_FLAGS\}\%#$(6)#g' | \
238	sed -E 's#\%\{CC_FLAGS\}\%#$(7)#g' > $$@
239
240$(PRJDIR)$(2)/arduino/examples/$(2)/$(2).ino: tensorflow/lite/micro/tools/make/templates/arduino_example.ino
241	@mkdir -p $$(dir $$@)
242	@cp $$< $$@
243
244$(PRJDIR)$(2)/arduino/src/TensorFlowLite.h: tensorflow/lite/micro/tools/make/templates/TensorFlowLite.h
245	@mkdir -p $$(dir $$@)
246	@cp $$< $$@
247
248# This would be cleaner if we broke up the list of dependencies into variables,
249# but these get hard to define with the evaluation approach used to define make
250# functions.
251generate_$(2)_arduino_project: \
252$(addprefix $(PRJDIR)$(2)/arduino/,         \
253$(patsubst tensorflow/%,src/tensorflow/%,\
254$(patsubst examples/%/main_functions.cpp,examples/%/main.ino,\
255$(patsubst examples/%_test.cpp,examples/%_test.ino,\
256$(patsubst tensorflow/lite/micro/examples/%,examples/%,\
257$(patsubst third_party/%,src/third_party/%,\
258$(patsubst %.cc,%.cpp,$(3))))))))                                     \
259$(addprefix $(PRJDIR)$(2)/arduino/, \
260$(patsubst tensorflow/%,src/tensorflow/%,\
261$(patsubst tensorflow/lite/micro/examples/%,examples/%,\
262$(patsubst third_party/%,src/third_party/%,$(4))))) \
263$(addprefix $(PRJDIR)$(2)/arduino/,$(1)) \
264$(PRJDIR)$(2)/arduino/src/TensorFlowLite.h
265
266generate_$(2)_arduino_library_zip: generate_$(2)_arduino_project
267	cp -r $(PRJDIR)$(2)/arduino $(PRJDIR)$(2)/tensorflow_lite
268	python tensorflow/lite/micro/tools/make/fix_arduino_subfolders.py $(PRJDIR)$(2)/tensorflow_lite
269	@cd $(PRJDIR)$(2) && zip -q -r tensorflow_lite.zip tensorflow_lite
270
271ALL_PROJECT_TARGETS += $(if $(findstring _test,$(2)),,generate_$(2)_arduino_library_zip)
272
273    ARDUINO_LIBRARY_ZIPS += $(if $(findstring _mock,$(2)),,$(if $(findstring _test,$(2)),,$(PRJDIR)$(2)/tensorflow_lite.zip))
274
275endef
276
277# Creates a set of rules to build a standalone ESP-IDF project for an
278# executable, including all of the source and header files required in a
279# separate folder.
280# Arguments are:
281# 1 - Project file template names.
282# 2 - Name of executable.
283# 3 - List of C/C++ source files needed to build the TF Micro component.
284# 4 - List of C/C++ header files needed to build the TF Micro component.
285# 5 - List of C/C++ source files needed to build this particular project.
286# 6 - List of C/C++ header files needed to build this particular project.
287# 7 - Linker flags required.
288# 8 - C++ compilation flags needed.
289# 9 - C compilation flags needed.
290# 10 - List of includes.
291define generate_esp_project
292$(PRJDIR)$(2)/esp-idf/LICENSE: LICENSE
293	@mkdir -p $$(dir $$@)
294	@cp $$< $$@
295
296$(PRJDIR)$(2)/esp-idf/main/%.cc: tensorflow/lite/micro/examples/$(2)/%.cc
297	@mkdir -p $$(dir $$@)
298	@python tensorflow/lite/micro/tools/make/transform_source.py \
299        --platform=esp \
300        --is_example_source \
301        --source_path="$$<" \
302        < $$< > $$@
303
304$(PRJDIR)$(2)/esp-idf/main/%: tensorflow/lite/micro/examples/$(2)/%
305	@mkdir -p $$(dir $$@)
306	@cp $$< $$@
307
308$(PRJDIR)$(2)/esp-idf/components/tfmicro/%: % third_party_downloads
309	@mkdir -p $$(dir $$@)
310	@cp $$< $$@
311
312$(PRJDIR)$(2)/esp-idf/components/tfmicro/third_party/%: tensorflow/lite/micro/tools/make/downloads/% third_party_downloads
313	@mkdir -p $$(dir $$@)
314	@cp $$< $$@
315
316$(PRJDIR)$(2)/esp-idf/%: tensorflow/lite/micro/tools/make/templates/esp/%.tpl
317	$(eval MAIN_SRCS_RELATIVE := $(patsubst tensorflow/lite/micro/examples/$(2)/%,%,$(5)))
318
319	@mkdir -p $$(dir $$@)
320	@sed -E 's#\%\{COMPONENT_SRCS\}\%#$(3)#g' $$< | \
321	sed -E 's#\%\{MAIN_SRCS\}\%#$(MAIN_SRCS_RELATIVE)#g' | \
322	sed -E 's#\%\{EXECUTABLE\}\%#$(2)#g' | \
323	sed -E 's#\%\{COMPONENT_INCLUDES\}\%#$(10)#g' | \
324	sed -E 's#\%\{LINKER_FLAGS\}\%#$(7)#g' | \
325	sed -E 's#\%\{CXX_FLAGS\}\%#$(8)#g' | \
326	sed -E 's#\%\{CC_FLAGS\}\%#$(9)#g' > $$@
327
328generate_$(2)_esp_project: \
329$(addprefix $(PRJDIR)$(2)/esp-idf/,\
330$(patsubst tensorflow/%,components/tfmicro/tensorflow/%,\
331$(patsubst third_party/%,components/tfmicro/third_party/%,\
332$(patsubst tensorflow/lite/micro/examples/$(2)/%,main/%,$(3) $(4) $(5) $(6))))) \
333$(addprefix $(PRJDIR)$(2)/esp-idf/,$(1))
334
335ALL_PROJECT_TARGETS += generate_$(2)_esp_project
336endef
337
338# Specialized version of generate_project for TF Lite Micro test targets that
339# automatically includes standard library files, so you just need to pass the
340# test name and any extra source files required.
341# Arguments are:
342# 1 - Name of test.
343# 2 - C/C++ source files implementing the test.
344# 3 - C/C++ header files needed for the test.
345# Calling eval on the output will create targets that you can invoke to
346# generate the standalone project.
347define generate_microlite_projects
348$(call generate_project,make,$(MAKE_PROJECT_FILES),$(1),$(MICROLITE_CC_SRCS) $(THIRD_PARTY_CC_SRCS) $(2),$(MICROLITE_CC_HDRS) $(THIRD_PARTY_CC_HDRS) $(MICROLITE_TEST_HDRS) $(3),$(LDFLAGS) $(MICROLITE_LIBS),$(CXXFLAGS) $(GENERATED_PROJECT_INCLUDES), $(CCFLAGS) $(GENERATED_PROJECT_INCLUDES))
349$(call generate_arc_project,make,$(MAKE_PROJECT_FILES),$(1),$(MICROLITE_CC_SRCS) $(THIRD_PARTY_CC_SRCS) $(2),$(MICROLITE_CC_HDRS) $(THIRD_PARTY_CC_HDRS) $(MICROLITE_TEST_HDRS) $(3),$(LDFLAGS) $(GENERATED_PROJECT_LIBS),$(CXXFLAGS) $(GENERATED_PROJECT_INCLUDES), $(CCFLAGS) $(GENERATED_PROJECT_INCLUDES))
350$(call generate_project,mbed,$(MBED_PROJECT_FILES),$(1),$(MICROLITE_CC_SRCS) $(THIRD_PARTY_CC_SRCS) $(2),$(MICROLITE_CC_HDRS) $(THIRD_PARTY_CC_HDRS) $(MICROLITE_TEST_HDRS) $(3),$(MICROLITE_LIBS),$(CXXFLAGS),$(CCFLAGS))
351$(call generate_project,keil,$(KEIL_PROJECT_FILES),$(1),$(MICROLITE_CC_SRCS) $(THIRD_PARTY_CC_SRCS) $(2),$(MICROLITE_CC_HDRS) $(THIRD_PARTY_CC_HDRS) $(MICROLITE_TEST_HDRS) $(3),$(MICROLITE_LIBS),$(CXXFLAGS),$(CCFLAGS))
352$(call generate_arduino_project,$(ARDUINO_PROJECT_FILES),$(1),$(MICROLITE_CC_SRCS) $(THIRD_PARTY_CC_SRCS) $(2),$(MICROLITE_CC_HDRS) $(THIRD_PARTY_CC_HDRS) $(MICROLITE_TEST_HDRS) $(3),$(MICROLITE_LIBS),$(CXXFLAGS),$(CCFLAGS))
353$(call generate_esp_project,$(ESP_PROJECT_FILES),$(1),$(MICROLITE_CC_SRCS) $(THIRD_PARTY_CC_SRCS),$(MICROLITE_CC_HDRS) $(THIRD_PARTY_CC_HDRS) $(MICROLITE_TEST_HDRS),$(2),$(3),$(MICROLITE_LIBS),$(CXXFLAGS),$(CCFLAGS),$(PROJECT_INCLUDES))
354endef
355
356# Handles the details of generating a binary target, including specializing
357# for the current platform, and generating project file targets.
358# Arguments are:
359# 1 - Name of test.
360# 2 - C/C++ source files implementing the test.
361# 3 - C/C++ header files needed for the test.
362# Calling eval on the output will create the targets that you need.
363define microlite_test
364$(1)_LOCAL_SRCS := $(2)
365$(1)_LOCAL_SRCS := $$(call specialize,$$($(1)_LOCAL_SRCS))
366ALL_SRCS += $$($(1)_LOCAL_SRCS)
367$(1)_LOCAL_HDRS := $(3)
368$(1)_LOCAL_OBJS := $$(addprefix $$(OBJDIR), \
369$$(patsubst %.cc,%.o,$$(patsubst %.c,%.o,$$($(1)_LOCAL_SRCS))))
370$(1)_BINARY := $$(BINDIR)$(1)
371$$($(1)_BINARY): $$($(1)_LOCAL_OBJS) $$(MICROLITE_LIB_PATH)
372	@mkdir -p $$(dir $$@)
373	$$(CXX) $$(CXXFLAGS) $$(INCLUDES) \
374	-o $$($(1)_BINARY) $$($(1)_LOCAL_OBJS) \
375	$$(LIBFLAGS) $$(MICROLITE_LIB_PATH) $$(LDFLAGS) $$(MICROLITE_LIBS)
376$(1): $$($(1)_BINARY)
377$(1)_bin: $$($(1)_BINARY).bin
378test_$(1): $$($(1)_BINARY)
379	@test -f $$(TEST_SCRIPT) || (echo 'Unable to find the test script. Is the software emulation available in $$(TARGET)?'; exit 1)
380	$$(TEST_SCRIPT) $$($(1)_BINARY) '~~~ALL TESTS PASSED~~~'
381ifneq (,$(findstring _test,$(1)))
382  MICROLITE_TEST_TARGETS += test_$(1)
383endif
384$(eval $(call generate_microlite_projects,$(1),$(call specialize,$(2)),$(3)))
385endef
386
387# Adds a dependency for a third-party library that needs to be downloaded from
388# an external source.
389# Arguments are:
390# 1 - URL to download archive file from (can be .zip, .tgz, or .bz).
391# 2 - MD5 sum of archive, to check integrity. Use md5sum tool to generate.
392# 3 - Folder name to unpack library into, inside tf/l/x/m/t/downloads root.
393# 4 - Optional patching action, must match clause in download_and_extract.sh.
394# 5 - Optional patching action parameter
395# These arguments are packed into a single '!' separated string, so no element
396# can contain a '!'.
397define add_third_party_download
398THIRD_PARTY_DOWNLOADS += $(1)!$(2)!tensorflow/lite/micro/tools/make/downloads/$(3)!$(4)!$(5)
399endef
400
401# Unpacks an entry in a list of strings created by add_third_party_download, and
402# defines a dependency rule to download the library. The download_and_extract.sh
403# script is used to handle to downloading and unpacking.
404# 1 - Information about the library, separated by '!'s.
405define create_download_rule
406$(word 3, $(subst !, ,$(1))):
407	tensorflow/lite/micro/tools/make/download_and_extract.sh $(subst !, ,$(1))
408THIRD_PARTY_TARGETS += $(word 3, $(subst !, ,$(1)))
409endef
410
411# Recursively find all files of given pattern
412# Arguments are:
413# 1 - Starting path
414# 2 - File pattern, e.g: *.h
415recursive_find = $(wildcard $(1)$(2)) $(foreach dir,$(wildcard $(1)*),$(call recursive_find,$(dir)/,$(2)))
416