Skip to content

Commit f03580c

Browse files
Fix library verification when installing from git repo or zip file (#1466)
* Fix library verification when installing from git repo or zip file * Enhance code comments and unit tests Co-authored-by: per1234 <[email protected]> Co-authored-by: per1234 <[email protected]>
1 parent 71ce1a4 commit f03580c

File tree

7 files changed

+133
-50
lines changed

7 files changed

+133
-50
lines changed

arduino/globals/globals.go

+7
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,11 @@ var (
4949
".cpp": empty,
5050
".S": empty,
5151
}
52+
53+
// HeaderFilesValidExtensions lists valid extensions for header files
54+
HeaderFilesValidExtensions = map[string]struct{}{
55+
".h": empty,
56+
".hpp": empty,
57+
".hh": empty,
58+
}
5259
)

arduino/libraries/libraries.go

+6-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"fmt"
2020

2121
"github.com/arduino/arduino-cli/arduino/cores"
22+
"github.com/arduino/arduino-cli/arduino/globals"
2223
"github.com/arduino/arduino-cli/i18n"
2324
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
2425
paths "github.com/arduino/go-paths-helper"
@@ -233,7 +234,11 @@ func (library *Library) SourceHeaders() ([]string, error) {
233234
if err != nil {
234235
return nil, fmt.Errorf(tr("reading lib src dir: %s"), err)
235236
}
236-
cppHeaders.FilterSuffix(".h", ".hpp", ".hh")
237+
headerExtensions := []string{}
238+
for k := range globals.HeaderFilesValidExtensions {
239+
headerExtensions = append(headerExtensions, k)
240+
}
241+
cppHeaders.FilterSuffix(headerExtensions...)
237242
res := []string{}
238243
for _, cppHeader := range cppHeaders {
239244
res = append(res, cppHeader.Base())

arduino/libraries/librariesmanager/install.go

+41-15
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"os"
2323
"strings"
2424

25+
"github.com/arduino/arduino-cli/arduino/globals"
2526
"github.com/arduino/arduino-cli/arduino/libraries"
2627
"github.com/arduino/arduino-cli/arduino/libraries/librariesindex"
2728
"github.com/arduino/arduino-cli/arduino/utils"
@@ -139,7 +140,7 @@ func (lm *LibrariesManager) InstallZipLib(ctx context.Context, archivePath strin
139140
extractionPath := paths[0]
140141
libraryName := extractionPath.Base()
141142

142-
if err := validateLibrary(libraryName, extractionPath); err != nil {
143+
if err := validateLibrary(extractionPath); err != nil {
143144
return err
144145
}
145146

@@ -229,7 +230,7 @@ func (lm *LibrariesManager) InstallGitLib(gitURL string, overwrite bool) error {
229230
return err
230231
}
231232

232-
if err := validateLibrary(libraryName, installPath); err != nil {
233+
if err := validateLibrary(installPath); err != nil {
233234
// Clean up installation directory since this is not a valid library
234235
installPath.RemoveAll()
235236
return err
@@ -259,22 +260,47 @@ func parseGitURL(gitURL string) (string, error) {
259260
return res, nil
260261
}
261262

262-
// validateLibrary verifies the dir contains a valid library, meaning it has both
263-
// an header <name>.h, either in src or root folder, and a library.properties file
264-
func validateLibrary(name string, dir *paths.Path) error {
265-
// Verify library contains library header.
266-
// Checks also root folder for legacy reasons.
267-
// For more info see:
263+
// validateLibrary verifies the dir contains a valid library, meaning it has either
264+
// library.properties file and an header in src/ or an header in its root folder.
265+
// Returns nil if dir contains a valid library, error on all other cases.
266+
func validateLibrary(dir *paths.Path) error {
267+
if dir.NotExist() {
268+
return fmt.Errorf(tr("directory doesn't exist: %s", dir))
269+
}
270+
271+
searchHeaderFile := func(d *paths.Path) (bool, error) {
272+
if d.NotExist() {
273+
// A directory that doesn't exist can't obviously contain any header file
274+
return false, nil
275+
}
276+
dirContent, err := d.ReadDir()
277+
if err != nil {
278+
return false, fmt.Errorf(tr("reading directory %s content: %w", dir, err))
279+
}
280+
dirContent.FilterOutDirs()
281+
headerExtensions := []string{}
282+
for k := range globals.HeaderFilesValidExtensions {
283+
headerExtensions = append(headerExtensions, k)
284+
}
285+
dirContent.FilterSuffix(headerExtensions...)
286+
return len(dirContent) > 0, nil
287+
}
288+
289+
// Recursive library layout
268290
// https://arduino.github.io/arduino-cli/latest/library-specification/#source-code
269-
libraryHeader := name + ".h"
270-
if !dir.Join("src", libraryHeader).Exist() && !dir.Join(libraryHeader).Exist() {
271-
return fmt.Errorf(tr(`library is not valid: missing header file "%s"`), libraryHeader)
291+
if headerFound, err := searchHeaderFile(dir.Join("src")); err != nil {
292+
return err
293+
} else if dir.Join("library.properties").Exist() && headerFound {
294+
return nil
272295
}
273296

274-
// Verifies library contains library.properties
275-
if !dir.Join("library.properties").Exist() {
276-
return fmt.Errorf(tr(`library is not valid: missing file "library.properties"`))
297+
// Flat library layout
298+
// https://arduino.github.io/arduino-cli/latest/library-specification/#source-code
299+
if headerFound, err := searchHeaderFile(dir); err != nil {
300+
return err
301+
} else if headerFound {
302+
return nil
277303
}
278304

279-
return nil
305+
return fmt.Errorf(tr("library not valid"))
280306
}

arduino/libraries/librariesmanager/install_test.go

+41
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package librariesmanager
1818
import (
1919
"testing"
2020

21+
"github.com/arduino/go-paths-helper"
2122
"github.com/stretchr/testify/require"
2223
)
2324

@@ -62,3 +63,43 @@ func TestParseGitURL(t *testing.T) {
6263
require.Equal(t, "arduino-lib", libraryName)
6364
require.NoError(t, err)
6465
}
66+
67+
func TestValidateLibrary(t *testing.T) {
68+
tmpDir := paths.New(t.TempDir())
69+
70+
nonExistingDirLib := tmpDir.Join("nonExistingDirLib")
71+
err := validateLibrary(nonExistingDirLib)
72+
require.Errorf(t, err, "directory doesn't exist: %s", nonExistingDirLib)
73+
74+
emptyLib := tmpDir.Join("emptyLib")
75+
emptyLib.Mkdir()
76+
err = validateLibrary(emptyLib)
77+
require.Errorf(t, err, "library not valid")
78+
79+
onlyPropertiesLib := tmpDir.Join("onlyPropertiesLib")
80+
onlyPropertiesLib.Mkdir()
81+
onlyPropertiesLib.Join("library.properties").WriteFile([]byte{})
82+
err = validateLibrary(onlyPropertiesLib)
83+
require.Errorf(t, err, "library not valid")
84+
85+
missingPropertiesLib := tmpDir.Join("missingPropertiesLib")
86+
missingPropertiesLibSourceDir := missingPropertiesLib.Join("src")
87+
missingPropertiesLibSourceDir.MkdirAll()
88+
missingPropertiesLibSourceDir.Join("some_file.hpp").WriteFile([]byte{})
89+
err = validateLibrary(missingPropertiesLib)
90+
require.Errorf(t, err, "library not valid")
91+
92+
validLib := tmpDir.Join("valiLib")
93+
validLibSourceDir := validLib.Join("src")
94+
validLibSourceDir.MkdirAll()
95+
validLibSourceDir.Join("some_file.hpp").WriteFile([]byte{})
96+
validLib.Join("library.properties").WriteFile([]byte{})
97+
err = validateLibrary(validLib)
98+
require.NoError(t, err)
99+
100+
validLegacyLib := tmpDir.Join("validLegacyLib")
101+
validLegacyLib.Mkdir()
102+
validLegacyLib.Join("some_file.hpp").WriteFile([]byte{})
103+
err = validateLibrary(validLib)
104+
require.NoError(t, err)
105+
}

i18n/data/en.po

+27-23
Original file line numberDiff line numberDiff line change
@@ -2238,10 +2238,10 @@ msgstr "Use the specified programmer to upload."
22382238
msgid "Used: {0}"
22392239
msgstr "Used: {0}"
22402240

2241-
#: arduino/libraries/librariesmanager/install.go:67
2242-
#: arduino/libraries/librariesmanager/install.go:83
2243-
#: arduino/libraries/librariesmanager/install.go:105
2244-
#: arduino/libraries/librariesmanager/install.go:189
2241+
#: arduino/libraries/librariesmanager/install.go:68
2242+
#: arduino/libraries/librariesmanager/install.go:84
2243+
#: arduino/libraries/librariesmanager/install.go:106
2244+
#: arduino/libraries/librariesmanager/install.go:190
22452245
msgid "User directory not set"
22462246
msgstr "User directory not set"
22472247

@@ -2355,7 +2355,7 @@ msgstr "Writing config file: %v"
23552355
msgid "archive hash differs from hash in index"
23562356
msgstr "archive hash differs from hash in index"
23572357

2358-
#: arduino/libraries/librariesmanager/install.go:136
2358+
#: arduino/libraries/librariesmanager/install.go:137
23592359
msgid "archive is not valid: multiple files found in zip file top level"
23602360
msgstr "archive is not valid: multiple files found in zip file top level"
23612361

@@ -2533,10 +2533,14 @@ msgstr "dependency '%s' is not available"
25332533
msgid "destination already exists"
25342534
msgstr "destination already exists"
25352535

2536-
#: arduino/libraries/librariesmanager/install.go:74
2536+
#: arduino/libraries/librariesmanager/install.go:75
25372537
msgid "destination dir %s already exists, cannot install"
25382538
msgstr "destination dir %s already exists, cannot install"
25392539

2540+
#: arduino/libraries/librariesmanager/install.go:268
2541+
msgid "directory doesn't exist: %s"
2542+
msgstr "directory doesn't exist: %s"
2543+
25402544
#: arduino/discovery/discoverymanager/discoverymanager.go:112
25412545
msgid "discovery %[1]s process not started: %[2]w"
25422546
msgstr "discovery %[1]s process not started: %[2]w"
@@ -2599,7 +2603,7 @@ msgstr "error: %s and %s flags cannot be used together"
25992603
msgid "extracting archive: %s"
26002604
msgstr "extracting archive: %s"
26012605

2602-
#: arduino/libraries/librariesmanager/install.go:124
2606+
#: arduino/libraries/librariesmanager/install.go:125
26032607
msgid "extracting archive: %w"
26042608
msgstr "extracting archive: %w"
26052609

@@ -2694,7 +2698,7 @@ msgstr "getting tool dependencies for platform %[1]s: %[2]s"
26942698
msgid "importing sketch metadata: %s"
26952699
msgstr "importing sketch metadata: %s"
26962700

2697-
#: arduino/libraries/librariesmanager/install.go:91
2701+
#: arduino/libraries/librariesmanager/install.go:92
26982702
msgid "install directory not set"
26992703
msgstr "install directory not set"
27002704

@@ -2755,7 +2759,7 @@ msgstr "invalid empty library version: %s"
27552759
msgid "invalid empty option found"
27562760
msgstr "invalid empty option found"
27572761

2758-
#: arduino/libraries/librariesmanager/install.go:257
2762+
#: arduino/libraries/librariesmanager/install.go:258
27592763
msgid "invalid git url"
27602764
msgstr "invalid git url"
27612765

@@ -2819,22 +2823,18 @@ msgstr "key not found in settings"
28192823
msgid "keywords"
28202824
msgstr "keywords"
28212825

2822-
#: arduino/libraries/librariesmanager/install.go:162
2823-
#: arduino/libraries/librariesmanager/install.go:205
2826+
#: arduino/libraries/librariesmanager/install.go:163
2827+
#: arduino/libraries/librariesmanager/install.go:206
28242828
msgid "library %s already installed"
28252829
msgstr "library %s already installed"
28262830

2827-
#: arduino/libraries/librariesmanager/install.go:37
2831+
#: arduino/libraries/librariesmanager/install.go:38
28282832
msgid "library already installed"
28292833
msgstr "library already installed"
28302834

2831-
#: arduino/libraries/librariesmanager/install.go:276
2832-
msgid "library is not valid: missing file \"library.properties\""
2833-
msgstr "library is not valid: missing file \"library.properties\""
2834-
2835-
#: arduino/libraries/librariesmanager/install.go:271
2836-
msgid "library is not valid: missing header file \"%s\""
2837-
msgstr "library is not valid: missing header file \"%s\""
2835+
#: arduino/libraries/librariesmanager/install.go:305
2836+
msgid "library not valid"
2837+
msgstr "library not valid"
28382838

28392839
#: arduino/libraries/librariesmanager/librariesmanager.go:226
28402840
msgid "library path does not exist: %s"
@@ -2917,7 +2917,7 @@ msgstr "missing platform release %[1]s:%[2]s referenced by board %[3]s"
29172917
msgid "monitor release not found: %s"
29182918
msgstr "monitor release not found: %s"
29192919

2920-
#: arduino/libraries/librariesmanager/install.go:179
2920+
#: arduino/libraries/librariesmanager/install.go:180
29212921
#: arduino/resources/install.go:94
29222922
msgid "moving extracted archive to destination dir: %s"
29232923
msgstr "moving extracted archive to destination dir: %s"
@@ -3077,6 +3077,10 @@ msgstr "reading dir %[1]s: %[2]s"
30773077
msgid "reading directory %[1]s: %[2]s"
30783078
msgstr "reading directory %[1]s: %[2]s"
30793079

3080+
#: arduino/libraries/librariesmanager/install.go:278
3081+
msgid "reading directory %s content: %w"
3082+
msgstr "reading directory %s content: %w"
3083+
30803084
#: arduino/builder/sketch.go:76
30813085
msgid "reading file %[1]s: %[2]s"
30823086
msgstr "reading file %[1]s: %[2]s"
@@ -3093,11 +3097,11 @@ msgstr "reading inventory file: %w"
30933097
msgid "reading lib headers: %s"
30943098
msgstr "reading lib headers: %s"
30953099

3096-
#: arduino/libraries/libraries.go:234
3100+
#: arduino/libraries/libraries.go:235
30973101
msgid "reading lib src dir: %s"
30983102
msgstr "reading lib src dir: %s"
30993103

3100-
#: arduino/libraries/libraries.go:114
3104+
#: arduino/libraries/libraries.go:115
31013105
msgid "reading library headers: %w"
31023106
msgstr "reading library headers: %w"
31033107

@@ -3135,7 +3139,7 @@ msgstr "release not found"
31353139
msgid "removing corrupted archive file: %s"
31363140
msgstr "removing corrupted archive file: %s"
31373141

3138-
#: arduino/libraries/librariesmanager/install.go:94
3142+
#: arduino/libraries/librariesmanager/install.go:95
31393143
msgid "removing lib directory: %s"
31403144
msgstr "removing lib directory: %s"
31413145

i18n/rice-box.go

+7-7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/test_lib.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -925,7 +925,7 @@ def test_install_zip_invalid_library(run_command, data_dir, downloads_dir):
925925
# Test zip-path install
926926
res = run_command(["lib", "install", "--zip-path", zip_path])
927927
assert res.failed
928-
assert 'library is not valid: missing header file "lib-without-header.h"' in res.stderr
928+
assert "library not valid" in res.stderr
929929

930930
lib_install_dir = Path(data_dir, "libraries", "lib-without-properties")
931931
# Verifies library is not already installed
@@ -935,7 +935,7 @@ def test_install_zip_invalid_library(run_command, data_dir, downloads_dir):
935935
# Test zip-path install
936936
res = run_command(["lib", "install", "--zip-path", zip_path])
937937
assert res.failed
938-
assert 'library is not valid: missing file "library.properties"' in res.stderr
938+
assert "library not valid" in res.stderr
939939

940940

941941
def test_install_git_invalid_library(run_command, data_dir, downloads_dir):
@@ -962,7 +962,7 @@ def test_install_git_invalid_library(run_command, data_dir, downloads_dir):
962962

963963
res = run_command(["lib", "install", "--git-url", repo_dir], custom_env=env)
964964
assert res.failed
965-
assert 'library is not valid: missing header file "lib-without-header.h"' in res.stderr
965+
assert "library not valid" in res.stderr
966966
assert not lib_install_dir.exists()
967967

968968
# Create another fake library repository
@@ -980,5 +980,5 @@ def test_install_git_invalid_library(run_command, data_dir, downloads_dir):
980980

981981
res = run_command(["lib", "install", "--git-url", repo_dir], custom_env=env)
982982
assert res.failed
983-
assert 'library is not valid: missing file "library.properties"' in res.stderr
983+
assert "library not valid" in res.stderr
984984
assert not lib_install_dir.exists()

0 commit comments

Comments
 (0)