Skip to content

Commit 3a614d7

Browse files
author
Federico Fissore
committed
A better implementation of ObjFileIsUpToDate, ported from OldCompiler
Fixes #19 Signed-off-by: Federico Fissore <[email protected]>
1 parent 1f368a8 commit 3a614d7

File tree

3 files changed

+249
-9
lines changed

3 files changed

+249
-9
lines changed

Diff for: src/arduino.cc/builder/builder_utils/utils.go

+95-9
Original file line numberDiff line numberDiff line change
@@ -141,17 +141,12 @@ func compileFileWithRecipe(sourcePath string, source string, buildPath string, b
141141
return "", utils.WrapError(err)
142142
}
143143

144-
sourceFileStat, err := os.Stat(properties[constants.BUILD_PROPERTIES_SOURCE_FILE])
144+
objIsUpToDate, err := ObjFileIsUpToDate(properties[constants.BUILD_PROPERTIES_SOURCE_FILE], properties[constants.BUILD_PROPERTIES_OBJECT_FILE], filepath.Join(buildPath, relativeSource+".d"))
145145
if err != nil {
146146
return "", utils.WrapError(err)
147147
}
148148

149-
objectFileStat, err := os.Stat(properties[constants.BUILD_PROPERTIES_OBJECT_FILE])
150-
if err != nil && !os.IsNotExist(err) {
151-
return "", utils.WrapError(err)
152-
}
153-
154-
if !objFileIsUpToDateWithSourceFile(sourceFileStat, objectFileStat) {
149+
if !objIsUpToDate {
155150
_, err = ExecRecipe(properties, recipe, false, verbose, verbose, logger)
156151
if err != nil {
157152
return "", utils.WrapError(err)
@@ -163,8 +158,99 @@ func compileFileWithRecipe(sourcePath string, source string, buildPath string, b
163158
return properties[constants.BUILD_PROPERTIES_OBJECT_FILE], nil
164159
}
165160

166-
func objFileIsUpToDateWithSourceFile(sourceFileStat, objectFileStat os.FileInfo) bool {
167-
return objectFileStat != nil && sourceFileStat.ModTime().Before(objectFileStat.ModTime())
161+
func ObjFileIsUpToDate(sourceFile, objectFile, dependencyFile string) (bool, error) {
162+
sourceFile = filepath.Clean(sourceFile)
163+
objectFile = filepath.Clean(objectFile)
164+
dependencyFile = filepath.Clean(dependencyFile)
165+
166+
sourceFileStat, err := os.Stat(sourceFile)
167+
if err != nil {
168+
return false, utils.WrapError(err)
169+
}
170+
171+
objectFileStat, err := os.Stat(objectFile)
172+
if err != nil {
173+
if os.IsNotExist(err) {
174+
return false, nil
175+
} else {
176+
return false, utils.WrapError(err)
177+
}
178+
}
179+
180+
dependencyFileStat, err := os.Stat(dependencyFile)
181+
if err != nil {
182+
if os.IsNotExist(err) {
183+
return false, nil
184+
} else {
185+
return false, utils.WrapError(err)
186+
}
187+
}
188+
189+
if sourceFileStat.ModTime().After(objectFileStat.ModTime()) {
190+
return false, nil
191+
}
192+
if sourceFileStat.ModTime().After(dependencyFileStat.ModTime()) {
193+
return false, nil
194+
}
195+
196+
rows, err := utils.ReadFileToRows(dependencyFile)
197+
if err != nil {
198+
return false, utils.WrapError(err)
199+
}
200+
201+
rows = utils.Map(rows, removeEndingBackSlash)
202+
rows = utils.Map(rows, strings.TrimSpace)
203+
rows = utils.Map(rows, unescapeDep)
204+
rows = utils.Filter(rows, nonEmptyString)
205+
206+
if len(rows) == 0 {
207+
return true, nil
208+
}
209+
210+
firstRow := rows[0]
211+
if !strings.HasSuffix(firstRow, ":") {
212+
return false, nil
213+
}
214+
objFileInDepFile := firstRow[:len(firstRow)-1]
215+
if objFileInDepFile != objectFile {
216+
return false, nil
217+
}
218+
219+
rows = rows[1:]
220+
for _, row := range rows {
221+
depStat, err := os.Stat(row)
222+
if err != nil && !os.IsNotExist(err) {
223+
return false, utils.WrapError(err)
224+
}
225+
if os.IsNotExist(err) {
226+
return false, nil
227+
}
228+
if depStat.ModTime().After(objectFileStat.ModTime()) {
229+
return false, nil
230+
}
231+
}
232+
233+
return true, nil
234+
}
235+
236+
func unescapeDep(s string) string {
237+
s = strings.Replace(s, "\\ ", " ", -1)
238+
s = strings.Replace(s, "\\\t", "\t", -1)
239+
s = strings.Replace(s, "\\#", "#", -1)
240+
s = strings.Replace(s, "$$", "$", -1)
241+
s = strings.Replace(s, "\\\\", "\\", -1)
242+
return s
243+
}
244+
245+
func removeEndingBackSlash(s string) string {
246+
if strings.HasSuffix(s, "\\") {
247+
s = s[:len(s)-1]
248+
}
249+
return s
250+
}
251+
252+
func nonEmptyString(s string) bool {
253+
return s != constants.EMPTY_STRING
168254
}
169255

170256
func ArchiveCompiledFiles(buildPath string, archiveFile string, objectFiles []string, buildProperties map[string]string, verbose bool, logger i18n.Logger) (string, error) {

Diff for: src/arduino.cc/builder/test/builder_utils_test.go

+142
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
package test
2+
3+
import (
4+
"arduino.cc/builder/builder_utils"
5+
"github.com/stretchr/testify/require"
6+
"io/ioutil"
7+
"os"
8+
"testing"
9+
"time"
10+
)
11+
12+
func sleep(t *testing.T) {
13+
dur, err := time.ParseDuration("300ms")
14+
NoError(t, err)
15+
time.Sleep(dur)
16+
}
17+
18+
func tempFile(t *testing.T, prefix string) string {
19+
file, err := ioutil.TempFile("", prefix)
20+
NoError(t, err)
21+
return file.Name()
22+
}
23+
24+
func TestObjFileIsUpToDateObjMissing(t *testing.T) {
25+
sourceFile := tempFile(t, "source")
26+
defer os.RemoveAll(sourceFile)
27+
28+
upToDate, err := builder_utils.ObjFileIsUpToDate(sourceFile, "", "")
29+
NoError(t, err)
30+
require.False(t, upToDate)
31+
}
32+
33+
func TestObjFileIsUpToDateDepMissing(t *testing.T) {
34+
sourceFile := tempFile(t, "source")
35+
defer os.RemoveAll(sourceFile)
36+
37+
objFile := tempFile(t, "obj")
38+
defer os.RemoveAll(objFile)
39+
40+
upToDate, err := builder_utils.ObjFileIsUpToDate(sourceFile, objFile, "")
41+
NoError(t, err)
42+
require.False(t, upToDate)
43+
}
44+
45+
func TestObjFileIsUpToDateObjOlder(t *testing.T) {
46+
objFile := tempFile(t, "obj")
47+
defer os.RemoveAll(objFile)
48+
depFile := tempFile(t, "dep")
49+
defer os.RemoveAll(depFile)
50+
51+
sleep(t)
52+
53+
sourceFile := tempFile(t, "source")
54+
defer os.RemoveAll(sourceFile)
55+
56+
upToDate, err := builder_utils.ObjFileIsUpToDate(sourceFile, objFile, depFile)
57+
NoError(t, err)
58+
require.False(t, upToDate)
59+
}
60+
61+
func TestObjFileIsUpToDateObjNewer(t *testing.T) {
62+
sourceFile := tempFile(t, "source")
63+
defer os.RemoveAll(sourceFile)
64+
65+
sleep(t)
66+
67+
objFile := tempFile(t, "obj")
68+
defer os.RemoveAll(objFile)
69+
depFile := tempFile(t, "dep")
70+
defer os.RemoveAll(depFile)
71+
72+
upToDate, err := builder_utils.ObjFileIsUpToDate(sourceFile, objFile, depFile)
73+
NoError(t, err)
74+
require.True(t, upToDate)
75+
}
76+
77+
func TestObjFileIsUpToDateDepIsNewer(t *testing.T) {
78+
sourceFile := tempFile(t, "source")
79+
defer os.RemoveAll(sourceFile)
80+
81+
sleep(t)
82+
83+
objFile := tempFile(t, "obj")
84+
defer os.RemoveAll(objFile)
85+
depFile := tempFile(t, "dep")
86+
defer os.RemoveAll(depFile)
87+
88+
sleep(t)
89+
90+
headerFile := tempFile(t, "header")
91+
defer os.RemoveAll(headerFile)
92+
93+
ioutil.WriteFile(depFile, []byte(objFile+": \\\n\t"+sourceFile+" \\\n\t"+headerFile), os.FileMode(0644))
94+
95+
upToDate, err := builder_utils.ObjFileIsUpToDate(sourceFile, objFile, depFile)
96+
NoError(t, err)
97+
require.False(t, upToDate)
98+
}
99+
100+
func TestObjFileIsUpToDateDepIsOlder(t *testing.T) {
101+
sourceFile := tempFile(t, "source")
102+
defer os.RemoveAll(sourceFile)
103+
104+
headerFile := tempFile(t, "header")
105+
defer os.RemoveAll(headerFile)
106+
107+
sleep(t)
108+
109+
objFile := tempFile(t, "obj")
110+
defer os.RemoveAll(objFile)
111+
depFile := tempFile(t, "dep")
112+
defer os.RemoveAll(depFile)
113+
114+
ioutil.WriteFile(depFile, []byte(objFile+": \\\n\t"+sourceFile+" \\\n\t"+headerFile), os.FileMode(0644))
115+
116+
upToDate, err := builder_utils.ObjFileIsUpToDate(sourceFile, objFile, depFile)
117+
NoError(t, err)
118+
require.True(t, upToDate)
119+
}
120+
121+
func TestObjFileIsUpToDateDepIsWrong(t *testing.T) {
122+
sourceFile := tempFile(t, "source")
123+
defer os.RemoveAll(sourceFile)
124+
125+
sleep(t)
126+
127+
objFile := tempFile(t, "obj")
128+
defer os.RemoveAll(objFile)
129+
depFile := tempFile(t, "dep")
130+
defer os.RemoveAll(depFile)
131+
132+
sleep(t)
133+
134+
headerFile := tempFile(t, "header")
135+
defer os.RemoveAll(headerFile)
136+
137+
ioutil.WriteFile(depFile, []byte(sourceFile+": \\\n\t"+sourceFile+" \\\n\t"+headerFile), os.FileMode(0644))
138+
139+
upToDate, err := builder_utils.ObjFileIsUpToDate(sourceFile, objFile, depFile)
140+
NoError(t, err)
141+
require.False(t, upToDate)
142+
}

Diff for: src/arduino.cc/builder/utils/utils.go

+12
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,18 @@ func Map(slice []string, fn mapFunc) []string {
206206
return newSlice
207207
}
208208

209+
type filterFunc func(string) bool
210+
211+
func Filter(slice []string, fn filterFunc) []string {
212+
newSlice := []string{}
213+
for _, elem := range slice {
214+
if fn(elem) {
215+
newSlice = append(newSlice, elem)
216+
}
217+
}
218+
return newSlice
219+
}
220+
209221
func WrapWithHyphenI(value string) string {
210222
return "\"-I" + value + "\""
211223
}

0 commit comments

Comments
 (0)