OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "net/test/python_utils.h" | |
6 | |
7 #include "base/base_paths.h" | |
8 #include "base/command_line.h" | |
9 #include "base/environment.h" | |
10 #include "base/files/file_path.h" | |
11 #include "base/files/file_util.h" | |
12 #include "base/lazy_instance.h" | |
13 #include "base/logging.h" | |
14 #include "base/memory/scoped_ptr.h" | |
15 #include "base/path_service.h" | |
16 #include "base/process/launch.h" | |
17 #include "base/strings/string_util.h" | |
18 #include "base/strings/utf_string_conversions.h" | |
19 | |
20 const char kPythonPathEnv[] = "PYTHONPATH"; | |
21 | |
22 void AppendToPythonPath(const base::FilePath& dir) { | |
23 scoped_ptr<base::Environment> env(base::Environment::Create()); | |
24 std::string old_path; | |
25 std::string dir_path; | |
26 #if defined(OS_WIN) | |
27 dir_path = base::WideToUTF8(dir.value()); | |
28 #elif defined(OS_POSIX) | |
29 dir_path = dir.value(); | |
30 #endif | |
31 if (!env->GetVar(kPythonPathEnv, &old_path)) { | |
32 env->SetVar(kPythonPathEnv, dir_path.c_str()); | |
33 } else if (old_path.find(dir_path) == std::string::npos) { | |
34 std::string new_path(old_path); | |
35 #if defined(OS_WIN) | |
36 new_path.append(";"); | |
37 #elif defined(OS_POSIX) | |
38 new_path.append(":"); | |
39 #endif | |
40 new_path.append(dir_path.c_str()); | |
41 env->SetVar(kPythonPathEnv, new_path); | |
42 } | |
43 } | |
44 | |
45 namespace { | |
46 | |
47 #if defined(OS_MACOSX) || defined(OS_CHROMEOS) | |
48 // Search for |to_try|, rolling up the directory tree from | |
49 // |start_dir|. If found, return true and put the path to |to_try| in | |
50 // |out_dir|. If not, return false and leave |out_dir| untouched. | |
51 bool TryRelativeToDir(const base::FilePath& start_dir, | |
52 const base::FilePath& to_try, | |
53 base::FilePath* out_dir) { | |
54 base::FilePath dir(start_dir); | |
55 while (!base::DirectoryExists(dir.Append(to_try))) { | |
56 base::FilePath parent = dir.DirName(); | |
57 if (parent == dir) { | |
58 // We hit the root directory. | |
59 return false; | |
60 } | |
61 dir = parent; | |
62 } | |
63 *out_dir = dir; | |
64 return true; | |
65 } | |
66 #endif // defined(OS_MACOSX) || defined(OS_CHROMEOS) | |
67 | |
68 } // namespace | |
69 | |
70 bool GetPyProtoPath(base::FilePath* dir) { | |
71 // Locate the Python code generated by the protocol buffers compiler. | |
72 base::FilePath generated_code_dir; | |
73 if (!PathService::Get(base::DIR_EXE, &generated_code_dir)) { | |
74 LOG(ERROR) << "Can't find " << generated_code_dir.value(); | |
75 return false; | |
76 } | |
77 | |
78 const base::FilePath kPyProto(FILE_PATH_LITERAL("pyproto")); | |
79 | |
80 #if defined(OS_MACOSX) || defined(OS_CHROMEOS) | |
81 base::FilePath source_dir; | |
82 if (!PathService::Get(base::DIR_SOURCE_ROOT, &source_dir)) { | |
83 LOG(ERROR) << "Can't find " << source_dir.value(); | |
84 return false; | |
85 } | |
86 // On Mac, and possibly Chrome OS, DIR_EXE might be pointing deep | |
87 // into the Release/ (or Debug/) directory and we can't depend on | |
88 // how far down it goes. So we walk upwards from DIR_EXE until we | |
89 // find a likely looking spot. | |
90 if (!TryRelativeToDir(generated_code_dir, kPyProto, dir)) { | |
91 LOG(WARNING) << "Can't find " << kPyProto.value() | |
92 << " next to " << generated_code_dir.value(); | |
93 // On Chrome OS, we may have installed the test binaries and support tools | |
94 // in a wholly separate location, relative to DIR_SOURCE_ROOT. We'll want | |
95 // to do a similar investigation from that point as well. | |
96 generated_code_dir = source_dir | |
97 .Append(FILE_PATH_LITERAL("out")) | |
98 .Append(FILE_PATH_LITERAL("Release")); | |
99 if (!TryRelativeToDir(generated_code_dir, kPyProto, dir)) { | |
100 LOG(WARNING) << "Can't find " << kPyProto.value() | |
101 << " next to " << generated_code_dir.value(); | |
102 return false; | |
103 } | |
104 } | |
105 generated_code_dir = *dir; | |
106 #endif | |
107 *dir = generated_code_dir.Append(kPyProto); | |
108 VLOG(2) << "Found " << kPyProto.value() << " in " << dir->value(); | |
109 return true; | |
110 } | |
111 | |
112 #if defined(OS_WIN) | |
113 struct PythonExePath { | |
114 PythonExePath() { | |
115 // This is test-only code, so CHECK with a subprocess invocation is ok. | |
116 base::CommandLine command(base::FilePath(FILE_PATH_LITERAL("cmd"))); | |
117 command.AppendArg("/c"); | |
118 command.AppendArg("python"); | |
119 command.AppendArg("-c"); | |
120 command.AppendArg("import sys; print sys.executable"); | |
121 std::string output; | |
122 CHECK(GetAppOutput(command, &output)); | |
123 // This does only work if cmd.exe doesn't use a non-US codepage. | |
124 path_ = base::ASCIIToUTF16(output); | |
125 TrimWhitespace(path_, base::TRIM_ALL, &path_); | |
126 } | |
127 base::string16 path_; | |
128 }; | |
129 static base::LazyInstance<PythonExePath>::Leaky g_python_path; | |
130 #endif | |
131 | |
132 bool GetPythonCommand(base::CommandLine* python_cmd) { | |
133 DCHECK(python_cmd); | |
134 | |
135 #if defined(OS_WIN) | |
136 // Most developers have depot_tools in their path, which only has a | |
137 // python.bat, not a python.exe. Go through cmd to find the path to | |
138 // the python executable. | |
139 // (Don't just return a a "cmd /c python" command line, because then tests | |
140 // that try to kill the python process will kill the cmd process instead, | |
141 // which can cause flakiness.) | |
142 python_cmd->SetProgram(base::FilePath(g_python_path.Get().path_)); | |
143 #else | |
144 python_cmd->SetProgram(base::FilePath(FILE_PATH_LITERAL("python"))); | |
145 #endif | |
146 | |
147 // Launch python in unbuffered mode, so that python output doesn't mix with | |
148 // gtest output in buildbot log files. See http://crbug.com/147368. | |
149 python_cmd->AppendArg("-u"); | |
150 | |
151 return true; | |
152 } | |
OLD | NEW |