@@ -34,8 +34,9 @@ type BuilderResult struct {
34
34
35
35
// UsedLibrary contains information regarding the library used during the compile process
36
36
type UsedLibrary struct {
37
- Name string `json:"name"`
38
- Version string `json:"version"`
37
+ Name string `json:"name"`
38
+ Version string `json:"version"`
39
+ ProvidesIncludes []string `json:"provides_includes"`
39
40
}
40
41
41
42
// BuildPlatform contains information regarding the platform used during the compile process
@@ -80,10 +81,10 @@ func compileSketch(cmd *cobra.Command, args []string) {
80
81
json .Unmarshal (cmdOutput , & unmarshalledOutput )
81
82
logrus .Infof ("arduino-cli version: %s" , unmarshalledOutput ["VersionString" ])
82
83
83
- // let's check if ar is installed on the users machine
84
- cmdOutput , err = exec .Command ("ar" , "--version" ).Output ()
84
+ // let's check if gcc- ar is installed on the users machine
85
+ cmdOutput , err = exec .Command ("gcc- ar" , "--version" ).CombinedOutput ()
85
86
if err != nil {
86
- logrus .Warn ("Before running this tool be sure to have \" GNU ar\" installed in your $PATH" )
87
+ logrus .Warn ("Before running this tool be sure to have \" gcc- ar\" installed in your $PATH" )
87
88
logrus .Fatal (err )
88
89
}
89
90
logrus .Infof (strings .Split (string (cmdOutput ), "\n " )[0 ]) // print the version of ar
@@ -110,8 +111,8 @@ func compileSketch(cmd *cobra.Command, args []string) {
110
111
111
112
// create a main.cpp file in the same dir of the sketch.ino
112
113
// the main.cpp contains the following:
113
- mainCpp := `
114
- #include "Arduino.h"
114
+ mainCpp :=
115
+ ` #include "Arduino.h"
115
116
void _setup();
116
117
void _loop();
117
118
@@ -128,9 +129,10 @@ _loop();
128
129
logrus .Fatal (err )
129
130
}
130
131
logrus .Infof ("created %s" , mainCppPath )
132
+ // TODO remove the cpp file after the compile
131
133
132
134
// replace setup() with _setup() and loop() with _loop() in the user's sketch.ino file
133
- // TODO make a backup copy of the sketch and restore it at the end (we have it in input var)
135
+ // TODO make a backup copy of the sketch and restore it after the compile (we have it in input var)
134
136
input , err := ioutil .ReadFile (inoPath .String ())
135
137
if err != nil {
136
138
logrus .Fatal (err )
@@ -153,14 +155,25 @@ _loop();
153
155
154
156
// let's call arduino-cli compile and parse the verbose output
155
157
cmdArgs := []string {"compile" , "-b" , fqbn , inoPath .String (), "-v" , "--format" , "json" }
156
- logrus .Infof ("running: arduino-cli %s" , cmdArgs )
158
+ logrus .Infof ("running: arduino-cli %s" , strings . Join ( cmdArgs , " " ) )
157
159
cmdOutput , err = exec .Command ("arduino-cli" , cmdArgs ... ).Output ()
158
160
if err != nil {
159
161
logrus .Fatal (err )
160
162
}
161
163
objFilesPaths , returnJson := parseCliCompileOutput (cmdOutput )
162
164
163
- // TODO remove the main.cpp file and restore the sketch ino file
165
+ // this is done to get the {build.mcu} used later to create the lib dir structure
166
+ // the --show-properties will only print on stdout and not compiling
167
+ // the json output is currently broken with this flag, see https://github.com/arduino/arduino-cli/issues/1628
168
+ cmdArgs = []string {"compile" , "-b" , fqbn , inoPath .String (), "--show-properties" }
169
+ logrus .Infof ("running: arduino-cli %s" , strings .Join (cmdArgs , " " ))
170
+ cmdOutput , err = exec .Command ("arduino-cli" , cmdArgs ... ).Output ()
171
+ if err != nil {
172
+ logrus .Fatal (err )
173
+ }
174
+ buildMcu := parseCliCompileOutputShowProp (cmdOutput )
175
+
176
+ // TODO remove the main.cpp file and restore the original sketch ino file
164
177
165
178
// we are going to leverage the precompiled library infrastructure to make the linking work.
166
179
// this type of lib, as the type suggest, is already compiled so it only gets linked during the linking phase of a sketch
@@ -174,45 +187,111 @@ _loop();
174
187
// ├── cortex-m0plus
175
188
// │ └── libsketch.a
176
189
// └── libsketch.h
177
- libName := strings .ToLower (inoPath .Base ())
190
+
191
+ // let's create the dir structure
192
+ sketchName := strings .ToLower (strings .TrimSuffix (inoPath .Base (), inoPath .Ext ()))
178
193
workingDir , err := paths .Getwd ()
179
194
if err != nil {
180
195
logrus .Fatal (err )
181
196
}
182
- libDir := workingDir .Join ("lib" + libName )
197
+ libDir := workingDir .Join ("lib" + sketchName )
183
198
if libDir .Exist () { // if the dir already exixst we clean it before
184
199
os .RemoveAll (libDir .String ())
185
200
logrus .Warn ("removed %s" , libDir )
186
201
}
187
202
if err = libDir .Mkdir (); err != nil {
188
203
logrus .Fatal (err )
189
204
}
205
+ srcDir := libDir .Join ("src" ).Join (buildMcu )
206
+ if err = srcDir .MkdirAll (); err != nil {
207
+ logrus .Fatal (err )
208
+ }
209
+ exampleDir := libDir .Join ("examples" ).Join (sketchName )
210
+ if err = exampleDir .MkdirAll (); err != nil {
211
+ logrus .Fatal (err )
212
+ }
213
+ extraDir := libDir .Join ("extra" )
214
+ if err = extraDir .Mkdir (); err != nil {
215
+ logrus .Fatal (err )
216
+ }
190
217
191
- // run ar to create an archive containing all the object files except the main.cpp.o (we'll create it later)
192
- // we exclude the main.cpp.o because we are going to link the archive libsjetch.a against another main.cpp
193
- objFilesPaths .FilterOutPrefix ("main.cpp" )
194
- // TODO use the correct name for the archive
195
- cmdArgs = append ([]string {"rcs" , buildDir .Join ("libsketch.a" ).String ()}, objFilesPaths .AsStrings ()... )
196
- logrus .Infof ("running: ar %s" , cmdArgs )
197
- cmdOutput , _ = exec .Command ("ar" , cmdArgs ... ).Output ()
198
- logrus .Print (cmdOutput )
199
-
200
- // Copy the object files from the `<tempdir>/arduino-sketch_stuff/sketch` folder
201
- for _ , objFilePath := range objFilesPaths {
202
- destObjFilePath := buildDir .Join (objFilePath .Base ())
203
- if err = objFilePath .CopyTo (destObjFilePath ); err != nil {
204
- logrus .Errorf ("error copying object file: %s" , err )
205
- } else {
206
- logrus .Infof ("copied file to %s" , destObjFilePath )
218
+ // let's create the files
219
+
220
+ // create a library.properties file in the root dir of the lib
221
+ // the library.properties contains the following:
222
+ libraryProperties :=
223
+ `name=` + sketchName + `
224
+ version=1.0
225
+ precompiled=true
226
+ `
227
+ libraryPropertyPath := libDir .Join ("library.properties" ).String ()
228
+ err = os .WriteFile (libraryPropertyPath , []byte (libraryProperties ), 0644 )
229
+ if err != nil {
230
+ logrus .Fatal (err )
231
+ }
232
+ logrus .Infof ("created %s" , libraryPropertyPath )
233
+
234
+ // we calculate the #include part to append at the beginning of the header file here
235
+ var librariesIncludes []string
236
+ for _ , lib := range returnJson .LibsInfo {
237
+ for _ , include := range lib .ProvidesIncludes {
238
+ librariesIncludes = append (librariesIncludes , "#include \" " + include + "\" " )
207
239
}
208
240
}
209
241
210
- // save the result.json in the build dir
211
- jsonFilePath := buildDir .Join ("result.json" )
242
+ // create the header file in the src/ dir
243
+ // This file has predeclarations of _setup() and _loop() functions declared originally in the main.cpp file (which is not included in the archive),
244
+ // It is the counterpart of libsketch.a
245
+ // the libsketch.h contains the following:
246
+ libsketchHeader := strings .Join (librariesIncludes , "\n " ) + `
247
+ void _setup();
248
+ void _loop();`
249
+ libsketchFilePath := srcDir .Parent ().Join ("lib" + sketchName + ".h" ).String ()
250
+ err = os .WriteFile (libsketchFilePath , []byte (libsketchHeader ), 0644 )
251
+ if err != nil {
252
+ logrus .Fatal (err )
253
+ }
254
+ logrus .Infof ("created %s" , libsketchFilePath )
255
+
256
+ // create the sketch file in the example dir of the lib
257
+ // This one will include the libsketch.h and basically is the replacement of main.cpp
258
+ // the sketch.ino contains the following:
259
+ sketchFile :=
260
+ `#include <` + "lib" + sketchName + `.h>
261
+ void setup() {
262
+ _setup();
263
+ }
264
+ void loop() {
265
+ _loop();
266
+ }`
267
+ sketchFilePath := exampleDir .Join (sketchName + ".ino" ).String ()
268
+ err = os .WriteFile (sketchFilePath , []byte (sketchFile ), 0644 )
269
+ if err != nil {
270
+ logrus .Fatal (err )
271
+ }
272
+ logrus .Infof ("created %s" , sketchFilePath )
273
+
274
+ // run gcc-ar to create an archive containing all the object files except the main.cpp.o (we'll create it later)
275
+ // we exclude the main.cpp.o because we are going to link the archive libsketch.a against another main.cpp
276
+ objFilesPaths .FilterOutPrefix ("main.cpp" )
277
+ archivePath := srcDir .Join ("lib" + sketchName + ".a" )
278
+ cmdArgs = append ([]string {"rcs" , archivePath .String ()}, objFilesPaths .AsStrings ()... )
279
+ logrus .Infof ("running: gcc-ar %s" , cmdArgs )
280
+ cmdOutput , err = exec .Command ("gcc-ar" , cmdArgs ... ).CombinedOutput ()
281
+ if err != nil {
282
+ logrus .Fatal (err )
283
+ }
284
+ if len (cmdOutput ) != 0 {
285
+ logrus .Info (string (cmdOutput ))
286
+ } else {
287
+ logrus .Infof ("created %s" , archivePath )
288
+ }
289
+ // save the result.json in the library extra dir
290
+ jsonFilePath := extraDir .Join ("result.json" )
212
291
if jsonContents , err := json .MarshalIndent (returnJson , "" , " " ); err != nil {
213
292
logrus .Errorf ("error serializing json: %s" , err )
214
293
} else if err := jsonFilePath .WriteFile (jsonContents ); err != nil {
215
- logrus .Errorf ("error writing result.json : %s" , err )
294
+ logrus .Errorf ("error writing %s : %s" , jsonFilePath . Base () , err )
216
295
} else {
217
296
logrus .Infof ("created new file in: %s" , jsonFilePath )
218
297
}
@@ -248,3 +327,21 @@ func parseCliCompileOutput(cmdOutToParse []byte) (paths.PathList, *ResultJson) {
248
327
249
328
return sketchFilesPaths , & returnJson
250
329
}
330
+
331
+ // parseCliCompileOutputShowProp function takes cmdOutToParse as argument,
332
+ // cmdOutToParse is the output of the command run
333
+ // the function extract the value corresponding to `build.mcu` key
334
+ // that string is returned if it's found. Otherwise the program exits
335
+ func parseCliCompileOutputShowProp (cmdOutToParse []byte ) string {
336
+ cmdOut := string (cmdOutToParse )
337
+ lines := strings .Split (cmdOut , "\n " )
338
+ for _ , line := range lines {
339
+ if strings .Contains (line , "build.mcu" ) { // the line should be something like: 'build.mcu=cortex-m0plus'
340
+ if mcuLine := strings .Split (line , "=" ); len (mcuLine ) == 2 {
341
+ return mcuLine [1 ]
342
+ }
343
+ }
344
+ }
345
+ logrus .Fatal ("cannot find \" build.mcu\" in arduino-cli output" )
346
+ return ""
347
+ }
0 commit comments