Skip to content

Commit a5e3bd9

Browse files
committed
WIP 🎉 it works! Still needs some code reorganization, update doc etc..
1 parent 432aa1f commit a5e3bd9

File tree

1 file changed

+128
-31
lines changed

1 file changed

+128
-31
lines changed

‎cmd/compile.go

Lines changed: 128 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,9 @@ type BuilderResult struct {
3434

3535
// UsedLibrary contains information regarding the library used during the compile process
3636
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"`
3940
}
4041

4142
// BuildPlatform contains information regarding the platform used during the compile process
@@ -80,10 +81,10 @@ func compileSketch(cmd *cobra.Command, args []string) {
8081
json.Unmarshal(cmdOutput, &unmarshalledOutput)
8182
logrus.Infof("arduino-cli version: %s", unmarshalledOutput["VersionString"])
8283

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()
8586
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")
8788
logrus.Fatal(err)
8889
}
8990
logrus.Infof(strings.Split(string(cmdOutput), "\n")[0]) // print the version of ar
@@ -110,8 +111,8 @@ func compileSketch(cmd *cobra.Command, args []string) {
110111

111112
// create a main.cpp file in the same dir of the sketch.ino
112113
// the main.cpp contains the following:
113-
mainCpp := `
114-
#include "Arduino.h"
114+
mainCpp :=
115+
`#include "Arduino.h"
115116
void _setup();
116117
void _loop();
117118
@@ -128,9 +129,10 @@ _loop();
128129
logrus.Fatal(err)
129130
}
130131
logrus.Infof("created %s", mainCppPath)
132+
// TODO remove the cpp file after the compile
131133

132134
// 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)
134136
input, err := ioutil.ReadFile(inoPath.String())
135137
if err != nil {
136138
logrus.Fatal(err)
@@ -153,14 +155,25 @@ _loop();
153155

154156
// let's call arduino-cli compile and parse the verbose output
155157
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, " "))
157159
cmdOutput, err = exec.Command("arduino-cli", cmdArgs...).Output()
158160
if err != nil {
159161
logrus.Fatal(err)
160162
}
161163
objFilesPaths, returnJson := parseCliCompileOutput(cmdOutput)
162164

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
164177

165178
// we are going to leverage the precompiled library infrastructure to make the linking work.
166179
// 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();
174187
// ├── cortex-m0plus
175188
// │ └── libsketch.a
176189
// └── 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()))
178193
workingDir, err := paths.Getwd()
179194
if err != nil {
180195
logrus.Fatal(err)
181196
}
182-
libDir := workingDir.Join("lib" + libName)
197+
libDir := workingDir.Join("lib" + sketchName)
183198
if libDir.Exist() { // if the dir already exixst we clean it before
184199
os.RemoveAll(libDir.String())
185200
logrus.Warn("removed %s", libDir)
186201
}
187202
if err = libDir.Mkdir(); err != nil {
188203
logrus.Fatal(err)
189204
}
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+
}
190217

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+"\"")
207239
}
208240
}
209241

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")
212291
if jsonContents, err := json.MarshalIndent(returnJson, "", " "); err != nil {
213292
logrus.Errorf("error serializing json: %s", err)
214293
} 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)
216295
} else {
217296
logrus.Infof("created new file in: %s", jsonFilePath)
218297
}
@@ -248,3 +327,21 @@ func parseCliCompileOutput(cmdOutToParse []byte) (paths.PathList, *ResultJson) {
248327

249328
return sketchFilesPaths, &returnJson
250329
}
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

Comments
 (0)