@@ -5,9 +5,12 @@ Copyright © 2021 NAME HERE <EMAIL ADDRESS>
5
5
package cmd
6
6
7
7
import (
8
+ "bytes"
8
9
"encoding/json"
10
+ "io/ioutil"
9
11
"os"
10
12
"os/exec"
13
+ "strings"
11
14
12
15
"github.com/arduino/go-paths-helper"
13
16
"github.com/sirupsen/logrus"
@@ -77,24 +80,122 @@ func compileSketch(cmd *cobra.Command, args []string) {
77
80
json .Unmarshal (cmdOutput , & unmarshalledOutput )
78
81
logrus .Infof ("arduino-cli version: %s" , unmarshalledOutput ["VersionString" ])
79
82
80
- // let's call arduino-cli compile and parse the verbose output
81
- logrus .Infof ("running: arduino-cli compile -b %s %s -v --format json" , fqbn , args [0 ])
82
- cmdOutput , err = exec .Command ("arduino-cli" , "compile" , "-b" , fqbn , args [0 ], "-v" , "--format" , "json" ).Output ()
83
+ // let's check if ar is installed on the users machine
84
+ cmdOutput , err = exec .Command ("ar" , "--version" ).Output ()
83
85
if err != nil {
86
+ logrus .Warn ("Before running this tool be sure to have \" GNU ar\" installed in your $PATH" )
84
87
logrus .Fatal (err )
85
88
}
86
- objFilesPaths , returnJson := parseOutput ( cmdOutput )
89
+ logrus . Infof ( strings . Split ( string ( cmdOutput ), " \n " )[ 0 ]) // print the version of ar
87
90
88
- workingDir , err := paths .Getwd ()
91
+ // check if the path of the sketch passed as args[0] is valid
92
+ sketchPath := paths .New (args [0 ])
93
+ if ! sketchPath .Exist () {
94
+ logrus .Fatalf ("the path %s do not exist!" , sketchPath )
95
+ }
96
+ var inoPath * paths.Path
97
+ if sketchPath .Ext () == ".ino" {
98
+ inoPath = sketchPath
99
+ } else { // if there are multiple .ino files in the sketchPath we need to know which is the one containing setup() and loop() functions
100
+ files , _ := sketchPath .ReadDir ()
101
+ files .FilterSuffix (".ino" )
102
+ if len (files ) == 0 {
103
+ logrus .Fatal ("the sketch path specified does not contain an .ino file" )
104
+ } else if len (files ) > 1 {
105
+ logrus .Fatalf ("the sketch path specified contains multiple .ino files:\n %s \n In order to make the magic please use the path of the .ino file containing the setup() and loop() functions" , strings .Join (files .AsStrings (), "\n " ))
106
+ }
107
+ inoPath = files [0 ]
108
+ }
109
+ logrus .Infof ("the ino file path is %s" , inoPath )
110
+
111
+ // create a main.cpp file in the same dir of the sketch.ino
112
+ // the main.cpp contains the following:
113
+ mainCpp := `
114
+ #include "Arduino.h"
115
+ void _setup();
116
+ void _loop();
117
+
118
+ void setup() {
119
+ _setup();
120
+ }
121
+
122
+ void loop() {
123
+ _loop();
124
+ }`
125
+ mainCppPath := inoPath .Parent ().Join ("main.cpp" ).String ()
126
+ err = os .WriteFile (mainCppPath , []byte (mainCpp ), 0644 )
89
127
if err != nil {
90
128
logrus .Fatal (err )
91
129
}
92
- buildDir := workingDir .Join ("build" )
93
- if ! buildDir .Exist () {
94
- if err = buildDir .Mkdir (); err != nil {
130
+ logrus .Infof ("created %s" , mainCppPath )
131
+
132
+ // 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)
134
+ input , err := ioutil .ReadFile (inoPath .String ())
135
+ if err != nil {
136
+ logrus .Fatal (err )
137
+ }
138
+ // TODO this check has meaning??
139
+ if bytes .Contains (input , []byte ("_setup()" )) {
140
+ logrus .Warnf ("already replaced setup() function in %s, skipping" , inoPath )
141
+ }
142
+ // TODO this check has meaning??
143
+ if bytes .Contains (input , []byte ("_loop()" )) {
144
+ logrus .Warnf ("already replaced loop() function in %s, skipping" , inoPath )
145
+ } else {
146
+ output := bytes .Replace (input , []byte ("void setup()" ), []byte ("void _setup()" ), - 1 )
147
+ output = bytes .Replace (output , []byte ("void loop()" ), []byte ("void _loop()" ), - 1 )
148
+ if err = ioutil .WriteFile (inoPath .String (), output , 0644 ); err != nil {
95
149
logrus .Fatal (err )
96
150
}
151
+ logrus .Infof ("replaced setup() and loop() functions in %s" , inoPath )
152
+ }
153
+
154
+ // let's call arduino-cli compile and parse the verbose output
155
+ cmdArgs := []string {"compile" , "-b" , fqbn , inoPath .String (), "-v" , "--format" , "json" }
156
+ logrus .Infof ("running: arduino-cli %s" , cmdArgs )
157
+ cmdOutput , err = exec .Command ("arduino-cli" , cmdArgs ... ).Output ()
158
+ if err != nil {
159
+ logrus .Fatal (err )
160
+ }
161
+ objFilesPaths , returnJson := parseCliCompileOutput (cmdOutput )
162
+
163
+ // TODO remove the main.cpp file and restore the sketch ino file
164
+
165
+ // we are going to leverage the precompiled library infrastructure to make the linking work.
166
+ // this type of lib, as the type suggest, is already compiled so it only gets linked during the linking phase of a sketch
167
+ // but we have to create a library folder structure in the current directory
168
+ // libsketch
169
+ // ├── examples
170
+ // │ └── sketch
171
+ // │ └── sketch.ino
172
+ // ├── library.properties
173
+ // └── src
174
+ // ├── cortex-m0plus
175
+ // │ └── libsketch.a
176
+ // └── libsketch.h
177
+ libName := strings .ToLower (inoPath .Base ())
178
+ workingDir , err := paths .Getwd ()
179
+ if err != nil {
180
+ logrus .Fatal (err )
181
+ }
182
+ libDir := workingDir .Join ("lib" + libName )
183
+ if libDir .Exist () { // if the dir already exixst we clean it before
184
+ os .RemoveAll (libDir .String ())
185
+ logrus .Warn ("removed %s" , libDir )
97
186
}
187
+ if err = libDir .Mkdir (); err != nil {
188
+ logrus .Fatal (err )
189
+ }
190
+
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 )
98
199
99
200
// Copy the object files from the `<tempdir>/arduino-sketch_stuff/sketch` folder
100
201
for _ , objFilePath := range objFilesPaths {
@@ -117,11 +218,11 @@ func compileSketch(cmd *cobra.Command, args []string) {
117
218
}
118
219
}
119
220
120
- // parseOutput function takes cmdOutToParse as argument,
221
+ // parseCliCompileOutput function takes cmdOutToParse as argument,
121
222
// cmdOutToParse is the json output captured from the command run
122
223
// the function extracts and returns the paths of the .o files
123
224
// (generated during the compile phase) and a ReturnJson object
124
- func parseOutput (cmdOutToParse []byte ) ([] * paths.Path , * ResultJson ) {
225
+ func parseCliCompileOutput (cmdOutToParse []byte ) (paths.PathList , * ResultJson ) {
125
226
var compileOutput CompileOutput
126
227
err := json .Unmarshal (cmdOutToParse , & compileOutput )
127
228
if err != nil {
0 commit comments