1// Copyright 2015 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// Implements the environment JSON file handling for serializing the
16// environment variables that were used in soong_build so that soong_ui can
17// check whether they have changed
18package shared
19
20import (
21	"encoding/json"
22	"fmt"
23	"io/ioutil"
24	"sort"
25)
26
27type envFileEntry struct{ Key, Value string }
28type envFileData []envFileEntry
29
30// Serializes the given environment variable name/value map into JSON formatted bytes by converting
31// to envFileEntry values and marshaling them.
32//
33// e.g. OUT_DIR = "out"
34// is converted to:
35//
36//	{
37//	    "Key": "OUT_DIR",
38//	    "Value": "out",
39//	},
40func EnvFileContents(envDeps map[string]string) ([]byte, error) {
41	contents := make(envFileData, 0, len(envDeps))
42	for key, value := range envDeps {
43		contents = append(contents, envFileEntry{key, value})
44	}
45
46	sort.Sort(contents)
47
48	data, err := json.MarshalIndent(contents, "", "    ")
49	if err != nil {
50		return nil, err
51	}
52
53	data = append(data, '\n')
54
55	return data, nil
56}
57
58// Reads and deserializes a Soong environment file located at the given file
59// path to determine its staleness. If any environment variable values have
60// changed, it prints and returns changed environment variable values and
61// returns true.
62// Failing to read or parse the file also causes it to return true.
63func StaleEnvFile(filepath string, getenv func(string) string) (isStale bool,
64	changedEnvironmentVariable []string, err error) {
65	data, err := ioutil.ReadFile(filepath)
66	if err != nil {
67		return true, nil, err
68	}
69
70	var contents envFileData
71
72	err = json.Unmarshal(data, &contents)
73	if err != nil {
74		return true, nil, err
75	}
76
77	var changed []string
78	for _, entry := range contents {
79		key := entry.Key
80		old := entry.Value
81		cur := getenv(key)
82		if old != cur {
83			changed = append(changed, fmt.Sprintf("%s (%q -> %q)", key, old, cur))
84			changedEnvironmentVariable = append(changedEnvironmentVariable, key)
85		}
86	}
87
88	if len(changed) > 0 {
89		fmt.Printf("environment variables changed value:\n")
90		for _, s := range changed {
91			fmt.Printf("   %s\n", s)
92		}
93		return true, changedEnvironmentVariable, nil
94	}
95
96	return false, nil, nil
97}
98
99// Deserializes and environment serialized by EnvFileContents() and returns it
100// as a map[string]string.
101func EnvFromFile(envFile string) (map[string]string, error) {
102	result := make(map[string]string)
103	data, err := ioutil.ReadFile(envFile)
104	if err != nil {
105		return result, err
106	}
107
108	var contents envFileData
109	err = json.Unmarshal(data, &contents)
110	if err != nil {
111		return result, err
112	}
113
114	for _, entry := range contents {
115		result[entry.Key] = entry.Value
116	}
117
118	return result, nil
119}
120
121// Implements sort.Interface so that we can use sort.Sort on envFileData arrays.
122func (e envFileData) Len() int {
123	return len(e)
124}
125
126func (e envFileData) Less(i, j int) bool {
127	return e[i].Key < e[j].Key
128}
129
130func (e envFileData) Swap(i, j int) {
131	e[i], e[j] = e[j], e[i]
132}
133
134var _ sort.Interface = envFileData{}
135