blob: 212293c864defee13a204b4d41ae96908b646b14 [file] [log] [blame]
Laszlo Csomor9dfb1ee2018-02-26 09:17:02 -08001// Copyright 2018 The Bazel Authors. 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.
Loo Rong Jie0a5ea612019-01-03 06:44:20 -080014
15// The "srcs_for_embedded_tools" rule in the same package sets the line below to
16// include runfiles.h from the correct path. Do not modify the line below.
17#include "tools/cpp/runfiles/runfiles_src.h"
Laszlo Csomor9dfb1ee2018-02-26 09:17:02 -080018
Loo Rong Jie4022bac2018-06-11 02:04:52 -070019#ifdef _WIN32
Laszlo Csomor1d698702018-03-19 02:14:33 -070020#include <windows.h>
Loo Rong Jie4022bac2018-06-11 02:04:52 -070021#else // not _WIN32
Laszlo Csomor1d698702018-03-19 02:14:33 -070022#include <stdlib.h>
23#include <sys/stat.h>
24#include <sys/types.h>
25#include <unistd.h>
Loo Rong Jie4022bac2018-06-11 02:04:52 -070026#endif // _WIN32
Laszlo Csomor1d698702018-03-19 02:14:33 -070027
Laszlo Csomore7d9e1f2018-02-28 06:48:18 -080028#include <fstream>
Loo Rong Jie0a5ea612019-01-03 06:44:20 -080029#include <functional>
Laszlo Csomore7d9e1f2018-02-28 06:48:18 -080030#include <map>
31#include <sstream>
Laszlo Csomor3bcad502018-03-05 06:35:20 -080032#include <vector>
Laszlo Csomore7d9e1f2018-02-28 06:48:18 -080033
Loo Rong Jie4022bac2018-06-11 02:04:52 -070034#ifdef _WIN32
Laszlo Csomor1d698702018-03-19 02:14:33 -070035#include <memory>
Loo Rong Jie4022bac2018-06-11 02:04:52 -070036#endif // _WIN32
Laszlo Csomor1d698702018-03-19 02:14:33 -070037
Laszlo Csomor9dfb1ee2018-02-26 09:17:02 -080038namespace bazel {
Laszlo Csomor819bf382018-04-30 03:29:21 -070039namespace tools {
40namespace cpp {
Laszlo Csomor9dfb1ee2018-02-26 09:17:02 -080041namespace runfiles {
42
Laszlo Csomor1d698702018-03-19 02:14:33 -070043using std::function;
Laszlo Csomore7d9e1f2018-02-28 06:48:18 -080044using std::map;
Laszlo Csomor3bcad502018-03-05 06:35:20 -080045using std::pair;
Laszlo Csomor9dfb1ee2018-02-26 09:17:02 -080046using std::string;
Laszlo Csomor3bcad502018-03-05 06:35:20 -080047using std::vector;
Laszlo Csomor9dfb1ee2018-02-26 09:17:02 -080048
49namespace {
50
Laszlo Csomor72075bf2018-05-24 02:59:54 -070051bool starts_with(const string& s, const char* prefix) {
52 if (!prefix || !*prefix) {
Laszlo Csomorf9cb8592018-04-24 05:52:37 -070053 return true;
54 }
55 if (s.empty()) {
56 return false;
57 }
58 return s.find(prefix) == 0;
59}
60
Laszlo Csomor72075bf2018-05-24 02:59:54 -070061bool contains(const string& s, const char* substr) {
62 if (!substr || !*substr) {
Laszlo Csomorf9cb8592018-04-24 05:52:37 -070063 return true;
64 }
65 if (s.empty()) {
66 return false;
67 }
68 return s.find(substr) != string::npos;
69}
70
71bool ends_with(const string& s, const string& suffix) {
72 if (suffix.empty()) {
73 return true;
74 }
75 if (s.empty()) {
76 return false;
77 }
78 return s.rfind(suffix) == s.size() - suffix.size();
79}
80
Laszlo Csomor1d698702018-03-19 02:14:33 -070081bool IsReadableFile(const string& path) {
82 return std::ifstream(path).is_open();
83}
84
85bool IsDirectory(const string& path) {
Loo Rong Jie4022bac2018-06-11 02:04:52 -070086#ifdef _WIN32
Laszlo Csomor1d698702018-03-19 02:14:33 -070087 DWORD attrs = GetFileAttributesA(path.c_str());
88 return (attrs != INVALID_FILE_ATTRIBUTES) &&
89 (attrs & FILE_ATTRIBUTE_DIRECTORY);
90#else
91 struct stat buf;
92 return stat(path.c_str(), &buf) == 0 && S_ISDIR(buf.st_mode);
93#endif
94}
95
Laszlo Csomor72075bf2018-05-24 02:59:54 -070096bool PathsFrom(const std::string& argv0, std::string runfiles_manifest_file,
Laszlo Csomor23bc3be2018-09-28 01:20:32 -070097 std::string runfiles_dir, std::string* out_manifest,
98 std::string* out_directory);
99
100bool PathsFrom(const std::string& argv0, std::string runfiles_manifest_file,
Laszlo Csomor72075bf2018-05-24 02:59:54 -0700101 std::string runfiles_dir,
102 std::function<bool(const std::string&)> is_runfiles_manifest,
103 std::function<bool(const std::string&)> is_runfiles_directory,
104 std::string* out_manifest, std::string* out_directory);
105
106bool ParseManifest(const string& path, map<string, string>* result,
107 string* error);
108
109} // namespace
110
111Runfiles* Runfiles::Create(const string& argv0,
112 const string& runfiles_manifest_file,
113 const string& runfiles_dir, string* error) {
Laszlo Csomor5f195b72018-05-18 08:03:18 -0700114 string manifest, directory;
Laszlo Csomor23bc3be2018-09-28 01:20:32 -0700115 if (!PathsFrom(argv0, runfiles_manifest_file, runfiles_dir, &manifest,
116 &directory)) {
Laszlo Csomor5f195b72018-05-18 08:03:18 -0700117 if (error) {
118 std::ostringstream err;
119 err << "ERROR: " << __FILE__ << "(" << __LINE__
120 << "): cannot find runfiles (argv0=\"" << argv0 << "\")";
121 *error = err.str();
122 }
123 return nullptr;
124 }
125
Laszlo Csomora1ae44a2018-05-22 04:53:01 -0700126 const vector<pair<string, string> > envvars = {
127 {"RUNFILES_MANIFEST_FILE", manifest},
128 {"RUNFILES_DIR", directory},
129 // TODO(laszlocsomor): remove JAVA_RUNFILES once the Java launcher can
130 // pick up RUNFILES_DIR.
131 {"JAVA_RUNFILES", directory}};
132
Laszlo Csomor9dc32e22018-05-23 08:04:26 -0700133 map<string, string> runfiles;
Laszlo Csomor1d698702018-03-19 02:14:33 -0700134 if (!manifest.empty()) {
Laszlo Csomor9dc32e22018-05-23 08:04:26 -0700135 if (!ParseManifest(manifest, &runfiles, error)) {
136 return nullptr;
137 }
Laszlo Csomor1d698702018-03-19 02:14:33 -0700138 }
Laszlo Csomor9dc32e22018-05-23 08:04:26 -0700139
Laszlo Csomor72075bf2018-05-24 02:59:54 -0700140 return new Runfiles(std::move(runfiles), std::move(directory),
141 std::move(envvars));
Laszlo Csomor1d698702018-03-19 02:14:33 -0700142}
143
Laszlo Csomor9dfb1ee2018-02-26 09:17:02 -0800144bool IsAbsolute(const string& path) {
145 if (path.empty()) {
146 return false;
147 }
148 char c = path.front();
Laszlo Csomorf9cb8592018-04-24 05:52:37 -0700149 return (c == '/' && (path.size() < 2 || path[1] != '/')) ||
150 (path.size() >= 3 &&
151 ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) &&
152 path[1] == ':' && (path[2] == '\\' || path[2] == '/'));
Laszlo Csomor9dfb1ee2018-02-26 09:17:02 -0800153}
154
Laszlo Csomor1d698702018-03-19 02:14:33 -0700155string GetEnv(const string& key) {
Loo Rong Jie4022bac2018-06-11 02:04:52 -0700156#ifdef _WIN32
Laszlo Csomor1d698702018-03-19 02:14:33 -0700157 DWORD size = ::GetEnvironmentVariableA(key.c_str(), NULL, 0);
158 if (size == 0) {
Laszlo Csomor23bc3be2018-09-28 01:20:32 -0700159 return string(); // unset or empty envvar
Laszlo Csomor1d698702018-03-19 02:14:33 -0700160 }
161 std::unique_ptr<char[]> value(new char[size]);
162 ::GetEnvironmentVariableA(key.c_str(), value.get(), size);
Laszlo Csomor23bc3be2018-09-28 01:20:32 -0700163 return value.get();
Laszlo Csomor1d698702018-03-19 02:14:33 -0700164#else
165 char* result = getenv(key.c_str());
Laszlo Csomor23bc3be2018-09-28 01:20:32 -0700166 return (result == NULL) ? string() : string(result);
Laszlo Csomor1d698702018-03-19 02:14:33 -0700167#endif
168}
169
Laszlo Csomor72075bf2018-05-24 02:59:54 -0700170string Runfiles::Rlocation(const string& path) const {
Laszlo Csomorf9cb8592018-04-24 05:52:37 -0700171 if (path.empty() || starts_with(path, "../") || contains(path, "/..") ||
172 starts_with(path, "./") || contains(path, "/./") ||
173 ends_with(path, "/.") || contains(path, "//")) {
Laszlo Csomor72075bf2018-05-24 02:59:54 -0700174 return string();
Laszlo Csomor9dfb1ee2018-02-26 09:17:02 -0800175 }
176 if (IsAbsolute(path)) {
177 return path;
178 }
Laszlo Csomore7d9e1f2018-02-28 06:48:18 -0800179 const auto value = runfiles_map_.find(path);
Laszlo Csomor9dc32e22018-05-23 08:04:26 -0700180 if (value != runfiles_map_.end()) {
181 return value->second;
182 }
183 if (!directory_.empty()) {
184 return directory_ + "/" + path;
185 }
186 return "";
Laszlo Csomore7d9e1f2018-02-28 06:48:18 -0800187}
188
Laszlo Csomor72075bf2018-05-24 02:59:54 -0700189namespace {
190
191bool ParseManifest(const string& path, map<string, string>* result,
192 string* error) {
Laszlo Csomore7d9e1f2018-02-28 06:48:18 -0800193 std::ifstream stm(path);
194 if (!stm.is_open()) {
195 if (error) {
196 std::ostringstream err;
197 err << "ERROR: " << __FILE__ << "(" << __LINE__
198 << "): cannot open runfiles manifest \"" << path << "\"";
199 *error = err.str();
200 }
201 return false;
202 }
203 string line;
204 std::getline(stm, line);
205 size_t line_count = 1;
206 while (!line.empty()) {
207 string::size_type idx = line.find_first_of(' ');
208 if (idx == string::npos) {
209 if (error) {
210 std::ostringstream err;
211 err << "ERROR: " << __FILE__ << "(" << __LINE__
212 << "): bad runfiles manifest entry in \"" << path << "\" line #"
213 << line_count << ": \"" << line << "\"";
214 *error = err.str();
215 }
216 return false;
217 }
218 (*result)[line.substr(0, idx)] = line.substr(idx + 1);
219 std::getline(stm, line);
220 ++line_count;
221 }
222 return true;
223}
224
Laszlo Csomor9dfb1ee2018-02-26 09:17:02 -0800225} // namespace
226
227namespace testing {
228
Laszlo Csomor72075bf2018-05-24 02:59:54 -0700229bool TestOnly_PathsFrom(const string& argv0, string mf, string dir,
230 function<bool(const string&)> is_runfiles_manifest,
231 function<bool(const string&)> is_runfiles_directory,
232 string* out_manifest, string* out_directory) {
233 return PathsFrom(argv0, mf, dir, is_runfiles_manifest, is_runfiles_directory,
234 out_manifest, out_directory);
Laszlo Csomor1d698702018-03-19 02:14:33 -0700235}
236
Laszlo Csomor9dfb1ee2018-02-26 09:17:02 -0800237bool TestOnly_IsAbsolute(const string& path) { return IsAbsolute(path); }
238
239} // namespace testing
240
Laszlo Csomor1d698702018-03-19 02:14:33 -0700241Runfiles* Runfiles::Create(const string& argv0, string* error) {
Laszlo Csomor72075bf2018-05-24 02:59:54 -0700242 return Runfiles::Create(argv0, GetEnv("RUNFILES_MANIFEST_FILE"),
243 GetEnv("RUNFILES_DIR"), error);
Laszlo Csomor1d698702018-03-19 02:14:33 -0700244}
245
Laszlo Csomor23bc3be2018-09-28 01:20:32 -0700246Runfiles* Runfiles::CreateForTest(std::string* error) {
247 return Runfiles::Create(std::string(), GetEnv("RUNFILES_MANIFEST_FILE"),
248 GetEnv("TEST_SRCDIR"), error);
249}
250
Laszlo Csomor72075bf2018-05-24 02:59:54 -0700251namespace {
252
Laszlo Csomor23bc3be2018-09-28 01:20:32 -0700253bool PathsFrom(const string& argv0, string mf, string dir, string* out_manifest,
254 string* out_directory) {
255 return PathsFrom(argv0, mf, dir,
256 [](const string& path) { return IsReadableFile(path); },
257 [](const string& path) { return IsDirectory(path); },
258 out_manifest, out_directory);
259}
260
Laszlo Csomor72075bf2018-05-24 02:59:54 -0700261bool PathsFrom(const string& argv0, string mf, string dir,
262 function<bool(const string&)> is_runfiles_manifest,
263 function<bool(const string&)> is_runfiles_directory,
264 string* out_manifest, string* out_directory) {
Laszlo Csomor9f2b0522018-05-02 08:07:14 -0700265 out_manifest->clear();
266 out_directory->clear();
Laszlo Csomor9f2b0522018-05-02 08:07:14 -0700267
268 bool mfValid = is_runfiles_manifest(mf);
269 bool dirValid = is_runfiles_directory(dir);
270
Laszlo Csomor23bc3be2018-09-28 01:20:32 -0700271 if (!argv0.empty() && !mfValid && !dirValid) {
Laszlo Csomor9f2b0522018-05-02 08:07:14 -0700272 mf = argv0 + ".runfiles/MANIFEST";
273 dir = argv0 + ".runfiles";
274 mfValid = is_runfiles_manifest(mf);
275 dirValid = is_runfiles_directory(dir);
276 if (!mfValid) {
277 mf = argv0 + ".runfiles_manifest";
278 mfValid = is_runfiles_manifest(mf);
279 }
280 }
281
282 if (!mfValid && !dirValid) {
283 return false;
284 }
285
286 if (!mfValid) {
287 mf = dir + "/MANIFEST";
288 mfValid = is_runfiles_manifest(mf);
289 if (!mfValid) {
290 mf = dir + "_manifest";
291 mfValid = is_runfiles_manifest(mf);
292 }
293 }
294
Laszlo Csomor23bc3be2018-09-28 01:20:32 -0700295 if (!dirValid &&
296 (ends_with(mf, ".runfiles_manifest") || ends_with(mf, "/MANIFEST"))) {
Laszlo Csomor9f2b0522018-05-02 08:07:14 -0700297 static const size_t kSubstrLen = 9; // "_manifest" or "/MANIFEST"
298 dir = mf.substr(0, mf.size() - kSubstrLen);
299 dirValid = is_runfiles_directory(dir);
300 }
301
302 if (mfValid) {
303 *out_manifest = mf;
304 }
305
306 if (dirValid) {
307 *out_directory = dir;
308 }
309
310 return true;
311}
312
Laszlo Csomor72075bf2018-05-24 02:59:54 -0700313} // namespace
314
Laszlo Csomor9dfb1ee2018-02-26 09:17:02 -0800315} // namespace runfiles
Laszlo Csomor819bf382018-04-30 03:29:21 -0700316} // namespace cpp
317} // namespace tools
Laszlo Csomor9dfb1ee2018-02-26 09:17:02 -0800318} // namespace bazel