aboutsummaryrefslogtreecommitdiff
path: root/compiler_wrapper/disable_werror_flag.go
blob: a5d22f52166e031e8e3ed31e3cfc234f7448e56a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
// Copyright 2019 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package main

import (
	"bytes"
	"encoding/json"
	"io/ioutil"
	"os"
	"strings"
)

func shouldForceDisableWError(env env) bool {
	return env.getenv("FORCE_DISABLE_WERROR") != ""
}

func doubleBuildWithWNoError(env env, cfg *config, originalCmd *command) (exitCode int, err error) {
	originalStdoutBuffer := &bytes.Buffer{}
	originalStderrBuffer := &bytes.Buffer{}
	// TODO: This is a bug in the old wrapper that it drops the ccache path
	// during double build. Fix this once we don't compare to the old wrapper anymore.
	if originalCmd.Path == "/usr/bin/ccache" {
		originalCmd.Path = "ccache"
	}
	originalStdinBuffer := &bytes.Buffer{}
	originalExitCode, err := wrapSubprocessErrorWithSourceLoc(originalCmd,
		env.run(originalCmd, teeStdinIfNeeded(env, originalCmd, originalStdinBuffer), originalStdoutBuffer, originalStderrBuffer))
	if err != nil {
		return 0, err
	}
	// The only way we can do anything useful is if it looks like the failure
	// was -Werror-related.
	if originalExitCode == 0 || !strings.Contains(originalStderrBuffer.String(), "-Werror") {
		originalStdoutBuffer.WriteTo(env.stdout())
		originalStderrBuffer.WriteTo(env.stderr())
		return originalExitCode, nil
	}

	retryStdoutBuffer := &bytes.Buffer{}
	retryStderrBuffer := &bytes.Buffer{}
	retryCommand := &command{
		Path:       originalCmd.Path,
		Args:       append(originalCmd.Args, "-Wno-error"),
		EnvUpdates: originalCmd.EnvUpdates,
	}
	retryExitCode, err := wrapSubprocessErrorWithSourceLoc(retryCommand,
		env.run(retryCommand, bytes.NewReader(originalStdinBuffer.Bytes()), retryStdoutBuffer, retryStderrBuffer))
	if err != nil {
		return 0, err
	}
	// If -Wno-error fixed us, pretend that we never ran without -Wno-error.
	// Otherwise, pretend that we never ran the second invocation. Since -Werror
	// is an issue, log in either case.
	if retryExitCode == 0 {
		retryStdoutBuffer.WriteTo(env.stdout())
		retryStderrBuffer.WriteTo(env.stderr())
	} else {
		originalStdoutBuffer.WriteTo(env.stdout())
		originalStderrBuffer.WriteTo(env.stderr())
	}

	// All of the below is basically logging. If we fail at any point, it's
	// reasonable for that to fail the build. This is all meant for FYI-like
	// builders in the first place.

	// Allow root and regular users to write to this without issue.
	if err := os.MkdirAll(cfg.newWarningsDir, 0777); err != nil {
		return 0, wrapErrorwithSourceLocf(err, "error creating warnings directory %s", cfg.newWarningsDir)
	}

	// Have some tag to show that files aren't fully written. It would be sad if
	// an interrupted build (or out of disk space, or similar) caused tools to
	// have to be overly-defensive.
	incompleteSuffix := ".incomplete"

	// Coming up with a consistent name for this is difficult (compiler command's
	// SHA can clash in the case of identically named files in different
	// directories, or similar); let's use a random one.
	tmpFile, err := ioutil.TempFile(cfg.newWarningsDir, "warnings_report*.json"+incompleteSuffix)
	if err != nil {
		return 0, wrapErrorwithSourceLocf(err, "error creating warnings file")
	}

	lines := []string{}
	if originalStderrBuffer.Len() > 0 {
		lines = append(lines, originalStderrBuffer.String())
	}
	if originalStdoutBuffer.Len() > 0 {
		lines = append(lines, originalStdoutBuffer.String())
	}
	outputToLog := strings.Join(lines, "\n")

	jsonData := warningsJSONData{
		Cwd:     env.getwd(),
		Command: append([]string{originalCmd.Path}, originalCmd.Args...),
		Stdout:  outputToLog,
	}
	enc := json.NewEncoder(tmpFile)
	if err := enc.Encode(jsonData); err != nil {
		_ = tmpFile.Close()
		return 0, wrapErrorwithSourceLocf(err, "error writing warnings data")
	}

	if err := tmpFile.Close(); err != nil {
		return 0, wrapErrorwithSourceLocf(err, "error closing warnings file")
	}

	if err := os.Rename(tmpFile.Name(), tmpFile.Name()[:len(tmpFile.Name())-len(incompleteSuffix)]); err != nil {
		return 0, wrapErrorwithSourceLocf(err, "error removing incomplete suffix from warnings file")
	}

	return retryExitCode, nil
}

// Struct used to write JSON. Fileds have to be uppercase for the json
// encoder to read them.
type warningsJSONData struct {
	Cwd     string   `json:"cwd"`
	Command []string `json:"command"`
	Stdout  string   `json:"stdout"`
}