Skip to content

Commit d594ab1

Browse files
committed
Fixed dependency file parsing on Windows
1 parent c1b290a commit d594ab1

File tree

4 files changed

+120
-43
lines changed

4 files changed

+120
-43
lines changed

Diff for: go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ require (
3939
go.bug.st/f v0.4.0
4040
go.bug.st/relaxed-semver v0.12.0
4141
go.bug.st/testifyjson v1.2.0
42+
golang.org/x/sys v0.26.0
4243
golang.org/x/term v0.25.0
4344
golang.org/x/text v0.19.0
4445
google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142
@@ -100,7 +101,6 @@ require (
100101
golang.org/x/mod v0.18.0 // indirect
101102
golang.org/x/net v0.28.0 // indirect
102103
golang.org/x/sync v0.8.0 // indirect
103-
golang.org/x/sys v0.26.0 // indirect
104104
golang.org/x/tools v0.22.0 // indirect
105105
gopkg.in/ini.v1 v1.67.0 // indirect
106106
gopkg.in/warnings.v0 v0.1.2 // indirect
+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// This file is part of arduino-cli.
2+
//
3+
// Copyright 2024 ARDUINO SA (http://www.arduino.cc/)
4+
//
5+
// This software is released under the GNU General Public License version 3,
6+
// which covers the main part of arduino-cli.
7+
// The terms of this license can be found at:
8+
// https://www.gnu.org/licenses/gpl-3.0.en.html
9+
//
10+
// You can be released from the requirements of the above licenses by purchasing
11+
// a commercial license. Buying such a license is mandatory if you want to
12+
// modify or otherwise use the software for commercial activities involving the
13+
// Arduino software without disclosing the source code of your own applications.
14+
// To purchase a commercial license, send an email to [email protected].
15+
16+
//go:build !windows
17+
18+
package utils
19+
20+
import (
21+
"errors"
22+
)
23+
24+
// placeholder for non-Windows machines
25+
func convertAnsiBytesToString([]byte) (string, error) {
26+
return "", errors.New("unimplemented")
27+
}
+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// This file is part of arduino-cli.
2+
//
3+
// Copyright 2024 ARDUINO SA (http://www.arduino.cc/)
4+
//
5+
// This software is released under the GNU General Public License version 3,
6+
// which covers the main part of arduino-cli.
7+
// The terms of this license can be found at:
8+
// https://www.gnu.org/licenses/gpl-3.0.en.html
9+
//
10+
// You can be released from the requirements of the above licenses by purchasing
11+
// a commercial license. Buying such a license is mandatory if you want to
12+
// modify or otherwise use the software for commercial activities involving the
13+
// Arduino software without disclosing the source code of your own applications.
14+
// To purchase a commercial license, send an email to [email protected].
15+
16+
package utils
17+
18+
import (
19+
"golang.org/x/sys/windows"
20+
)
21+
22+
func convertAnsiBytesToString(data []byte) (string, error) {
23+
dataSize := int32(len(data))
24+
size, err := windows.MultiByteToWideChar(windows.GetACP(), 0, &data[0], dataSize, nil, 0)
25+
if err != nil {
26+
return "", err
27+
}
28+
utf16 := make([]uint16, size)
29+
if _, err := windows.MultiByteToWideChar(windows.GetACP(), 0, &data[0], dataSize, &utf16[0], size); err != nil {
30+
return "", err
31+
}
32+
return windows.UTF16ToString(utf16), nil
33+
}

Diff for: internal/arduino/builder/internal/utils/utils.go

+59-42
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package utils
1717

1818
import (
1919
"os"
20+
"runtime"
2021
"strings"
2122
"unicode"
2223

@@ -74,63 +75,79 @@ func ObjFileIsUpToDate(sourceFile, objectFile, dependencyFile *paths.Path) (bool
7475
return false, nil
7576
}
7677

77-
rows, err := dependencyFile.ReadFileAsLines()
78+
depFileData, err := dependencyFile.ReadFile()
7879
if err != nil {
7980
logrus.Debugf("Could not read dependency file: %s", dependencyFile)
8081
return false, err
8182
}
8283

83-
rows = f.Map(rows, removeEndingBackSlash)
84-
rows = f.Map(rows, strings.TrimSpace)
85-
rows = f.Map(rows, unescapeDep)
86-
rows = f.Filter(rows, f.NotEquals(""))
84+
checkDepFile := func(depFile string) (bool, error) {
85+
rows := strings.Split(strings.Replace(depFile, "\r\n", "\n", -1), "\n")
86+
rows = f.Map(rows, removeEndingBackSlash)
87+
rows = f.Map(rows, strings.TrimSpace)
88+
rows = f.Map(rows, unescapeDep)
89+
rows = f.Filter(rows, f.NotEquals(""))
8790

88-
if len(rows) == 0 {
89-
return true, nil
90-
}
91-
92-
firstRow := rows[0]
93-
if !strings.HasSuffix(firstRow, ":") {
94-
logrus.Debugf("No colon in first line of depfile")
95-
return false, nil
96-
}
97-
objFileInDepFile := firstRow[:len(firstRow)-1]
98-
if objFileInDepFile != objectFile.String() {
99-
logrus.Debugf("Depfile is about different object file: %v", objFileInDepFile)
100-
return false, nil
101-
}
102-
103-
// The first line of the depfile contains the path to the object file to generate.
104-
// The second line of the depfile contains the path to the source file.
105-
// All subsequent lines contain the header files necessary to compile the object file.
106-
107-
// If we don't do this check it might happen that trying to compile a source file
108-
// that has the same name but a different path wouldn't recreate the object file.
109-
if sourceFile.String() != strings.Trim(rows[1], " ") {
110-
logrus.Debugf("Depfile is about different source file: %v", strings.Trim(rows[1], " "))
111-
return false, nil
112-
}
91+
if len(rows) == 0 {
92+
return true, nil
93+
}
11394

114-
rows = rows[1:]
115-
for _, row := range rows {
116-
depStat, err := os.Stat(row)
117-
if err != nil && !os.IsNotExist(err) {
118-
// There is probably a parsing error of the dep file
119-
// Ignore the error and trigger a full rebuild anyway
120-
logrus.WithError(err).Debugf("Failed to read: %v", row)
95+
firstRow := rows[0]
96+
if !strings.HasSuffix(firstRow, ":") {
97+
logrus.Debugf("No colon in first line of depfile")
12198
return false, nil
12299
}
123-
if os.IsNotExist(err) {
124-
logrus.Debugf("Not found: %v", row)
100+
objFileInDepFile := firstRow[:len(firstRow)-1]
101+
if objFileInDepFile != objectFile.String() {
102+
logrus.Debugf("Depfile is about different object file: %v", objFileInDepFile)
125103
return false, nil
126104
}
127-
if depStat.ModTime().After(objectFileStat.ModTime()) {
128-
logrus.Debugf("%v newer than %v", row, objectFile)
105+
106+
// The first line of the depfile contains the path to the object file to generate.
107+
// The second line of the depfile contains the path to the source file.
108+
// All subsequent lines contain the header files necessary to compile the object file.
109+
110+
// If we don't do this check it might happen that trying to compile a source file
111+
// that has the same name but a different path wouldn't recreate the object file.
112+
if sourceFile.String() != strings.Trim(rows[1], " ") {
113+
logrus.Debugf("Depfile is about different source file: %v", strings.Trim(rows[1], " "))
129114
return false, nil
130115
}
116+
117+
rows = rows[1:]
118+
for _, row := range rows {
119+
depStat, err := os.Stat(row)
120+
if err != nil && !os.IsNotExist(err) {
121+
// There is probably a parsing error of the dep file
122+
// Ignore the error and trigger a full rebuild anyway
123+
logrus.WithError(err).Debugf("Failed to read: %v", row)
124+
return false, nil
125+
}
126+
if os.IsNotExist(err) {
127+
logrus.Debugf("Not found: %v", row)
128+
return false, nil
129+
}
130+
if depStat.ModTime().After(objectFileStat.ModTime()) {
131+
logrus.Debugf("%v newer than %v", row, objectFile)
132+
return false, nil
133+
}
134+
}
135+
136+
return true, nil
131137
}
132138

133-
return true, nil
139+
if runtime.GOOS == "windows" {
140+
// This is required because on Windows we don't know which encoding is used
141+
// by gcc to write the dep file (it could be UTF-8 or any of the Windows
142+
// ANSI mappings).
143+
if decoded, err := convertAnsiBytesToString(depFileData); err == nil {
144+
if upToDate, err := checkDepFile(string(decoded)); err == nil && upToDate {
145+
return upToDate, nil
146+
}
147+
}
148+
// Fallback to UTF-8...
149+
}
150+
return checkDepFile(string(depFileData))
134151
}
135152

136153
func removeEndingBackSlash(s string) string {

0 commit comments

Comments
 (0)