Skip to content

Commit 47fad88

Browse files
committed
Add library.properties parsing
1 parent fe5f4f9 commit 47fad88

File tree

5 files changed

+145
-0
lines changed

5 files changed

+145
-0
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1010
- Add `__AVR__` to defines when compiling
1111
- `arduino_ci_remote.rb` CLI switch `--skip-examples-compilation`
1212
- `CppLibrary.header_files` to find header files
13+
- `LibraryProperties` to read metadata from Arduino libraries
1314

1415
### Changed
1516
- Move repository from https://github.com/ianfixes/arduino_ci to https://github.com/Arduino-CI/arduino_ci

lib/arduino_ci.rb

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
require "arduino_ci/arduino_installation"
33
require "arduino_ci/cpp_library"
44
require "arduino_ci/ci_config"
5+
require "arduino_ci/library_properties"
56

67
# ArduinoCI contains classes for automated testing of Arduino code on the command line
78
# @author Ian Katz <[email protected]>

lib/arduino_ci/library_properties.rb

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
module ArduinoCI
2+
3+
# Information about an Arduino library package, as specified by the library.properties file
4+
#
5+
# See https://arduino.github.io/arduino-cli/library-specification/#libraryproperties-file-format
6+
class LibraryProperties
7+
8+
# @return [Hash] The properties file parsed as a hash
9+
attr_reader :fields
10+
11+
# @param path [Pathname] The path to the library.properties file
12+
def initialize(path)
13+
@fields = {}
14+
File.foreach(path) do |line|
15+
parts = line.split("=", 2)
16+
@fields[parts[0]] = parts[1].chomp unless parts.empty?
17+
end
18+
end
19+
20+
# Enable a shortcut syntax for library property accessors, in the style of `attr_accessor` metaprogramming.
21+
# This is used to create a named field pointing to a specific property in the file, optionally applying
22+
# a specific formatting function.
23+
#
24+
# The formatting function MUST be a static method on this class. This is a limitation caused by the desire
25+
# to both (1) expose the formatters outside this class, and (2) use them for metaprogramming without the
26+
# having to name the entire function. field_reader is a static method, so if not for the fact that
27+
# `self.class.methods.include? formatter` fails to work for class methods in this context (unlike
28+
# `self.methods.include?`, which properly finds instance methods), I would allow either one and just
29+
# conditionally `define_method` the proper definition
30+
#
31+
# @param name [String] What the accessor will be called
32+
# @param field_num [Integer] The name of the key of the property
33+
# @param formatter [Symbol] The symbol for the formatting function to apply to the field (optional)
34+
# @return [void]
35+
# @macro [attach] field_reader
36+
# @!attribute [r] $1
37+
# @return property $2 of the library.properties file, formatted with the function {$3}
38+
def self.field_reader(name, formatter = nil)
39+
key = name.to_s
40+
if formatter.nil?
41+
define_method(name) { @fields[key] }
42+
else
43+
define_method(name) { @fields.key?(key) ? self.class.send(formatter.to_sym, @fields[key]) : nil }
44+
end
45+
end
46+
47+
# Parse a value as a comma-separated array
48+
# @param input [String]
49+
# @return [Array<String>] The individual values
50+
def self._csv(input)
51+
input.split(",").map(&:strip)
52+
end
53+
54+
# Parse a value as a boolean
55+
# @param input [String]
56+
# @return [Array<String>] The individual values
57+
def self._bool(input)
58+
input == "true" # no indication given in the docs that anything but lowercase "true" indicates boolean true.
59+
end
60+
61+
field_reader :name
62+
field_reader :version
63+
field_reader :author, :_csv
64+
field_reader :maintainer
65+
field_reader :sentence
66+
field_reader :paragraph
67+
field_reader :category
68+
field_reader :url
69+
field_reader :architectures, :_csv
70+
field_reader :depends, :_csv
71+
field_reader :dot_a_linkage, :_bool
72+
field_reader :includes, :_csv
73+
field_reader :precompiled, :_bool
74+
field_reader :ldflags, :_csv
75+
76+
# The value of sentence always will be prepended, so you should start by writing the second sentence here
77+
#
78+
# (according to the docs)
79+
# @return [String] the sentence and paragraph together
80+
def full_paragraph
81+
[sentence, paragraph].join(" ")
82+
end
83+
84+
end
85+
86+
end

spec/library_properties_spec.rb

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
require "spec_helper"
2+
3+
RSpec.describe ArduinoCI::LibraryProperties do
4+
5+
context "property extraction" do
6+
library_properties = ArduinoCI::LibraryProperties.new(Pathname.new(__dir__) + "properties/example.library.properties")
7+
8+
expected = {
9+
string: {
10+
name: "WebServer",
11+
version: "1.0.0",
12+
maintainer: "Cristian Maglie <[email protected]>",
13+
sentence: "A library that makes coding a Webserver a breeze.",
14+
paragraph: "Supports HTTP1.1 and you can do GET and POST.",
15+
category: "Communication",
16+
url: "http://example.com/",
17+
},
18+
19+
bool: {
20+
precompiled: true
21+
},
22+
23+
csv: {
24+
author: ["Cristian Maglie <[email protected]>", "Pippo Pluto <[email protected]>"],
25+
architectures: ["avr"],
26+
includes: ["WebServer.h"],
27+
depends: ["ArduinoHttpClient"],
28+
},
29+
}.freeze
30+
31+
expected.each do |atype, values|
32+
values.each do |meth, val|
33+
it "reads #{atype} field #{meth}" do
34+
expect(library_properties.send(meth)).to eq(val)
35+
end
36+
end
37+
end
38+
39+
it "doesn't crash on nonexistent fields" do
40+
expect(library_properties.dot_a_linkage).to be(nil)
41+
end
42+
end
43+
44+
45+
end
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
name=WebServer
2+
version=1.0.0
3+
author=Cristian Maglie <[email protected]>, Pippo Pluto <[email protected]>
4+
maintainer=Cristian Maglie <[email protected]>
5+
sentence=A library that makes coding a Webserver a breeze.
6+
paragraph=Supports HTTP1.1 and you can do GET and POST.
7+
category=Communication
8+
url=http://example.com/
9+
architectures=avr
10+
includes=WebServer.h
11+
depends=ArduinoHttpClient
12+
precompiled=true

0 commit comments

Comments
 (0)