Skip to content

Commit 2b61e9c

Browse files
committed
Add recursive library dependency compilation
1 parent 47fad88 commit 2b61e9c

File tree

7 files changed

+91
-12
lines changed

7 files changed

+91
-12
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1111
- `arduino_ci_remote.rb` CLI switch `--skip-examples-compilation`
1212
- `CppLibrary.header_files` to find header files
1313
- `LibraryProperties` to read metadata from Arduino libraries
14+
- `CppLibrary.library_properties_path`, `CppLibrary.library_properties?`, `CppLibrary.library_properties` to expose library properties of a Cpp library
15+
- `CppLibrary.arduino_library_dependencies` to list the dependent libraries specified by the library.properties file
1416

1517
### Changed
1618
- Move repository from https://github.com/ianfixes/arduino_ci to https://github.com/Arduino-CI/arduino_ci
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
depends=OnePointOhDummy,OnePointFiveDummy

SampleProjects/DependOnSomething/src/YesDeps.cpp

Whitespace-only changes.

SampleProjects/DependOnSomething/src/YesDeps.h

Whitespace-only changes.

SampleProjects/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ Because of this, these projects include some intentional quirks that differ from
1111
* "OnePointOhDummy" is a non-functional library meant to test file inclusion logic on libraries conforming to the "1.0" specification
1212
* "OnePointFiveMalformed" is a non-functional library meant to test file inclusion logic on libraries that attempt to conform to the ["1.5" specfication](https://arduino.github.io/arduino-cli/latest/library-specification/) but fail to include a `src` directory
1313
* "OnePointFiveDummy" is a non-functional library meant to test file inclusion logic on libraries conforming to the ["1.5" specfication](https://arduino.github.io/arduino-cli/latest/library-specification/)
14+
* "DependOnSomething" is a non-functional library meant to test file inclusion logic with dependencies

lib/arduino_ci/cpp_library.rb

+45-6
Original file line numberDiff line numberDiff line change
@@ -55,16 +55,30 @@ def initialize(base_dir, arduino_lib_dir, exclude_dirs)
5555
@vendor_bundle_cache = nil
5656
end
5757

58+
# The expected path to the library.properties file (i.e. even if it does not exist)
59+
# @return [Pathname]
60+
def library_properties_path
61+
@base_dir + "library.properties"
62+
end
63+
64+
# Whether library.properties definitions for this library exist
65+
# @return [bool]
66+
def library_properties?
67+
lib_props = library_properties_path
68+
lib_props.exist? && lib_props.file?
69+
end
70+
5871
# Decide whether this is a 1.5-compatible library
5972
#
6073
# according to https://arduino.github.io/arduino-cli/latest/library-specification
6174
#
6275
# Should match logic from https://github.com/arduino/arduino-cli/blob/master/arduino/libraries/loader.go
6376
# @return [bool]
6477
def one_point_five?
65-
lib_props = (@base_dir + "library.properties")
78+
return false unless library_properties?
79+
6680
src_dir = (@base_dir + "src")
67-
[lib_props, src_dir].all?(&:exist?) && lib_props.file? && src_dir.directory?
81+
src_dir.exist? && src_dir.directory?
6882
end
6983

7084
# Guess whether a file is part of the vendor bundle (indicating we should ignore it).
@@ -164,6 +178,21 @@ def libasan?(gcc_binary)
164178
@has_libasan_cache[gcc_binary]
165179
end
166180

181+
# Library properties
182+
def library_properties
183+
return nil unless library_properties?
184+
185+
LibraryProperties.new(library_properties_path)
186+
end
187+
188+
# Get a list of all dependencies as defined in library.properties
189+
# @return [Array<String>] The library names of the dependencies (not the paths)
190+
def arduino_library_dependencies
191+
return nil unless library_properties?
192+
193+
library_properties.depends
194+
end
195+
167196
# Get a list of all CPP source files in a directory and its subdirectories
168197
# @param some_dir [Pathname] The directory in which to begin the search
169198
# @param extensions [Array<Sring>] The set of allowable file extensions
@@ -282,16 +311,19 @@ def gcc_version(gcc_binary)
282311
@last_err
283312
end
284313

285-
# Arduino library directories containing sources
314+
# Arduino library directories containing sources -- only those of the dependencies
286315
# @return [Array<Pathname>]
287316
def arduino_library_src_dirs(aux_libraries)
288317
# Pull in all possible places that headers could live, according to the spec:
289318
# https://github.com/arduino/Arduino/wiki/Arduino-IDE-1.5:-Library-specification
290319

291-
aux_libraries.map { |d| self.new(d, @arduino_lib_dir, @exclude_dirs).header_dirs }.flatten
320+
aux_libraries.map { |d| self.class.new(@arduino_lib_dir + d, @arduino_lib_dir, @exclude_dirs).header_dirs }.flatten.uniq
292321
end
293322

294323
# GCC command line arguments for including aux libraries
324+
#
325+
# This function recursively collects the library directores of the dependencies
326+
#
295327
# @param aux_libraries [Array<Pathname>] The external Arduino libraries required by this project
296328
# @return [Array<String>] The GCC command-line flags necessary to include those libraries
297329
def include_args(aux_libraries)
@@ -354,6 +386,9 @@ def test_args(aux_libraries, ci_gcc_config)
354386
end
355387

356388
# build a file for running a test of the given unit test file
389+
#
390+
# The dependent libraries configuration is appended with data from library.properties internal to the library under test
391+
#
357392
# @param test_file [Pathname] The path to the file containing the unit tests
358393
# @param aux_libraries [Array<Pathname>] The external Arduino libraries required by this project
359394
# @param ci_gcc_config [Hash] The GCC config object
@@ -372,8 +407,12 @@ def build_for_test_with_configuration(test_file, aux_libraries, gcc_binary, ci_g
372407
"-fsanitize=address"
373408
]
374409
end
375-
arg_sets << test_args(aux_libraries, ci_gcc_config)
376-
arg_sets << cpp_files_libraries(aux_libraries).map(&:to_s)
410+
411+
# combine library.properties defs (if existing) with config file.
412+
# TODO: as much as I'd like to rely only on the properties file(s), I think that would prevent testing 1.0-spec libs
413+
full_aux_libraries = arduino_library_dependencies.nil? ? aux_libraries : aux_libaries + arduino_library_dependencies
414+
arg_sets << test_args(full_aux_libraries, ci_gcc_config)
415+
arg_sets << cpp_files_libraries(full_aux_libraries).map(&:to_s)
377416
arg_sets << [test_file.to_s]
378417
args = arg_sets.flatten(1)
379418
return nil unless run_gcc(gcc_binary, *args)

spec/cpp_library_spec.rb

+42-6
Original file line numberDiff line numberDiff line change
@@ -14,58 +14,79 @@ def get_relative_dir(sampleprojects_tests_dir)
1414
next if skip_ruby_tests
1515

1616
answers = {
17-
"DoSomething": {
17+
DoSomething: {
1818
one_five: false,
1919
cpp_files: [Pathname.new("DoSomething") + "do-something.cpp"],
20+
cpp_files_libraries: [],
2021
header_dirs: [Pathname.new("DoSomething")],
22+
arduino_library_src_dirs: [],
2123
test_files: [
2224
"DoSomething/test/good-null.cpp",
2325
"DoSomething/test/good-library.cpp",
2426
"DoSomething/test/bad-null.cpp",
2527
].map { |f| Pathname.new(f) }
2628
},
27-
"OnePointOhDummy": {
29+
OnePointOhDummy: {
2830
one_five: false,
2931
cpp_files: [
3032
"OnePointOhDummy/YesBase.cpp",
3133
"OnePointOhDummy/utility/YesUtil.cpp",
3234
].map { |f| Pathname.new(f) },
35+
cpp_files_libraries: [],
3336
header_dirs: [
3437
"OnePointOhDummy",
3538
"OnePointOhDummy/utility"
3639
].map { |f| Pathname.new(f) },
40+
arduino_library_src_dirs: [],
3741
test_files: []
3842
},
39-
"OnePointFiveMalformed": {
43+
OnePointFiveMalformed: {
4044
one_five: false,
4145
cpp_files: [
4246
"OnePointFiveMalformed/YesBase.cpp",
4347
"OnePointFiveMalformed/utility/YesUtil.cpp",
4448
].map { |f| Pathname.new(f) },
49+
cpp_files_libraries: [],
4550
header_dirs: [
4651
"OnePointFiveMalformed",
4752
"OnePointFiveMalformed/utility"
4853
].map { |f| Pathname.new(f) },
54+
arduino_library_src_dirs: [],
4955
test_files: []
5056
},
51-
"OnePointFiveDummy": {
57+
OnePointFiveDummy: {
5258
one_five: true,
5359
cpp_files: [
5460
"OnePointFiveDummy/src/YesSrc.cpp",
5561
"OnePointFiveDummy/src/subdir/YesSubdir.cpp",
5662
].map { |f| Pathname.new(f) },
63+
cpp_files_libraries: [],
5764
header_dirs: [
5865
"OnePointFiveDummy/src",
5966
"OnePointFiveDummy/src/subdir",
6067
].map { |f| Pathname.new(f) },
68+
arduino_library_src_dirs: [],
6169
test_files: []
6270
}
63-
}.freeze
71+
}
72+
73+
# easier to construct this one from the other test cases
74+
answers[:DependOnSomething] = {
75+
one_five: true,
76+
cpp_files: ["DependOnSomething/src/YesDeps.cpp"].map { |f| Pathname.new(f) },
77+
cpp_files_libraries: answers[:OnePointOhDummy][:cpp_files] + answers[:OnePointFiveDummy][:cpp_files],
78+
header_dirs: ["DependOnSomething/src"].map { |f| Pathname.new(f) }, # this is not recursive!
79+
arduino_library_src_dirs: answers[:OnePointOhDummy][:header_dirs] + answers[:OnePointFiveDummy][:header_dirs],
80+
test_files: []
81+
}
82+
83+
answers.freeze
6484

6585
answers.each do |sampleproject, expected|
6686
context "#{sampleproject}" do
6787
cpp_lib_path = sampleproj_path + sampleproject.to_s
68-
cpp_library = ArduinoCI::CppLibrary.new(cpp_lib_path, Pathname.new("my_fake_arduino_lib_dir"), [])
88+
cpp_library = ArduinoCI::CppLibrary.new(cpp_lib_path, sampleproj_path, [])
89+
dependencies = cpp_library.arduino_library_dependencies.nil? ? [] : cpp_library.arduino_library_dependencies
6990

7091
it "detects 1.5 format" do
7192
expect(cpp_library.one_point_five?).to eq(expected[:one_five])
@@ -78,6 +99,13 @@ def get_relative_dir(sampleprojects_tests_dir)
7899
end
79100
end
80101

102+
context "cpp_files_libraries" do
103+
it "finds cpp files in directories of dependencies" do
104+
relative_paths = cpp_library.cpp_files_libraries(dependencies).map { |f| get_relative_dir(f) }
105+
expect(relative_paths.map(&:to_s)).to match_array(expected[:cpp_files_libraries].map(&:to_s))
106+
end
107+
end
108+
81109
context "header_dirs" do
82110
it "finds directories containing h files" do
83111
relative_paths = cpp_library.header_dirs.map { |f| get_relative_dir(f) }
@@ -101,6 +129,14 @@ def get_relative_dir(sampleprojects_tests_dir)
101129
expect(relative_paths.map(&:to_s)).to match_array(expected[:test_files].map(&:to_s))
102130
end
103131
end
132+
133+
context "arduino_library_src_dirs" do
134+
it "finds src dirs from dependent libraries" do
135+
# we explicitly feed in the internal dependencies
136+
relative_paths = cpp_library.arduino_library_src_dirs(dependencies).map { |f| get_relative_dir(f) }
137+
expect(relative_paths.map(&:to_s)).to match_array(expected[:arduino_library_src_dirs].map(&:to_s))
138+
end
139+
end
104140
end
105141
end
106142

0 commit comments

Comments
 (0)