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//     "Key": "OUT_DIR",
37//     "Value": "out",
38// },
39func EnvFileContents(envDeps map[string]string) ([]byte, error) {
40	contents := make(envFileData, 0, len(envDeps))
41	for key, value := range envDeps {
42		contents = append(contents, envFileEntry{key, value})
43	}
44
45	sort.Sort(contents)
46
47	data, err := json.MarshalIndent(contents, "", "    ")
48	if err != nil {
49		return nil, err
50	}
51
52	data = append(data, '\n')
53
54	return data, nil
55}
56
57// Reads and deserializes a Soong environment file located at the given file path to determine its
58// staleness. If any environment variable values have changed, it prints them out and returns true.
59// Failing to read or parse the file also causes it to return true.
60func StaleEnvFile(filepath string, getenv func(string) string) (bool, error) {
61	data, err := ioutil.ReadFile(filepath)
62	if err != nil {
63		return true, err
64	}
65
66	var contents envFileData
67
68	err = json.Unmarshal(data, &contents)
69	if err != nil {
70		return true, err
71	}
72
73	var changed []string
74	for _, entry := range contents {
75		key := entry.Key
76		old := entry.Value
77		cur := getenv(key)
78		if old != cur {
79			changed = append(changed, fmt.Sprintf("%s (%q -> %q)", key, old, cur))
80		}
81	}
82
83	if len(changed) > 0 {
84		fmt.Printf("environment variables changed value:\n")
85		for _, s := range changed {
86			fmt.Printf("   %s\n", s)
87		}
88		return true, nil
89	}
90
91	return false, nil
92}
93
94// Deserializes and environment serialized by EnvFileContents() and returns it
95// as a map[string]string.
96func EnvFromFile(envFile string) (map[string]string, error) {
97	result := make(map[string]string)
98	data, err := ioutil.ReadFile(envFile)
99	if err != nil {
100		return result, err
101	}
102
103	var contents envFileData
104	err = json.Unmarshal(data, &contents)
105	if err != nil {
106		return result, err
107	}
108
109	for _, entry := range contents {
110		result[entry.Key] = entry.Value
111	}
112
113	return result, nil
114}
115
116// Implements sort.Interface so that we can use sort.Sort on envFileData arrays.
117func (e envFileData) Len() int {
118	return len(e)
119}
120
121func (e envFileData) Less(i, j int) bool {
122	return e[i].Key < e[j].Key
123}
124
125func (e envFileData) Swap(i, j int) {
126	e[i], e[j] = e[j], e[i]
127}
128
129var _ sort.Interface = envFileData{}
130