comparison src/org/tmatesoft/hg/internal/Internals.java @ 379:fa2be7a05af6

Implement discovery of mercurial installation, use it to read/write configuration files
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Thu, 09 Feb 2012 16:47:17 +0100
parents 9fb990c8a724
children 82336b7c54f4
comparison
equal deleted inserted replaced
378:9fb990c8a724 379:fa2be7a05af6
20 20
21 import java.io.File; 21 import java.io.File;
22 import java.io.FileOutputStream; 22 import java.io.FileOutputStream;
23 import java.io.IOException; 23 import java.io.IOException;
24 import java.util.ArrayList; 24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.Collections;
27 import java.util.LinkedHashSet;
25 import java.util.List; 28 import java.util.List;
29 import java.util.StringTokenizer;
26 30
27 import org.tmatesoft.hg.repo.HgInternals; 31 import org.tmatesoft.hg.repo.HgInternals;
28 import org.tmatesoft.hg.repo.HgRepoConfig.ExtensionsSection; 32 import org.tmatesoft.hg.repo.HgRepoConfig.ExtensionsSection;
29 import org.tmatesoft.hg.repo.HgRepository; 33 import org.tmatesoft.hg.repo.HgRepository;
30 import org.tmatesoft.hg.util.PathRewrite; 34 import org.tmatesoft.hg.util.PathRewrite;
34 * 38 *
35 * @author Artem Tikhomirov 39 * @author Artem Tikhomirov
36 * @author TMate Software Ltd. 40 * @author TMate Software Ltd.
37 */ 41 */
38 public class Internals { 42 public class Internals {
43
44 public static final String CFG_PROPERTY_HG_INSTALL_ROOT = "hg4j.hg.install_root";
39 45
40 private int requiresFlags = 0; 46 private int requiresFlags = 0;
41 private List<Filter.Factory> filterFactories; 47 private List<Filter.Factory> filterFactories;
42 48
43 49
112 } 118 }
113 119
114 public static boolean runningOnWindows() { 120 public static boolean runningOnWindows() {
115 return System.getProperty("os.name").indexOf("Windows") != -1; 121 return System.getProperty("os.name").indexOf("Windows") != -1;
116 } 122 }
117 123
124 /**
125 * For Unix, returns installation root, which is the parent directory of the hg executable (or symlink) being run.
126 * For Windows, it's Mercurial installation directory itself
127 */
128 private static File findHgInstallRoot() {
129 // let clients to override Hg install location
130 String p = System.getProperty(CFG_PROPERTY_HG_INSTALL_ROOT);
131 if (p != null) {
132 return new File(p);
133 }
134 StringTokenizer st = new StringTokenizer(System.getenv("PATH"), System.getProperty("path.separator"), false);
135 final boolean runsOnWin = runningOnWindows();
136 while (st.hasMoreTokens()) {
137 String pe = st.nextToken();
138 File execCandidate = new File(pe, runsOnWin ? "hg.exe" : "hg");
139 if (execCandidate.exists() && execCandidate.isFile()) {
140 File execDir = execCandidate.getParentFile();
141 // e.g. on Unix runs "/shared/tools/bin/hg", directory of interest is "/shared/tools/"
142 return runsOnWin ? execDir : execDir.getParentFile();
143 }
144 }
145 return null;
146 }
147
148 /**
149 * @see http://www.selenic.com/mercurial/hgrc.5.html
150 */
118 public ConfigFile readConfiguration(HgRepository hgRepo, File repoRoot) throws IOException { 151 public ConfigFile readConfiguration(HgRepository hgRepo, File repoRoot) throws IOException {
119 ConfigFile configFile = new ConfigFile(); 152 ConfigFile configFile = new ConfigFile();
153 File hgInstallRoot = findHgInstallRoot(); // may be null
154 //
120 if (runningOnWindows()) { 155 if (runningOnWindows()) {
121 // FIXME read install-dir 156 if (hgInstallRoot != null) {
122 // 157 for (File f : getWindowsConfigFilesPerInstall(hgInstallRoot)) {
123 // XXX perhaps, makes sense to compare getenv(USERPROFILE) and getenv(HOME) and use 158 configFile.addLocation(f);
124 // them if set (and use both if their values do not match). Only if both not set, rely to user.home? 159 }
125 // Also respect #getUserConfigurationFileToWrite() below 160 }
126 configFile.addLocation(new File(System.getProperty("user.home"), "Mercurial.ini")); 161 LinkedHashSet<String> locations = new LinkedHashSet<String>();
162 locations.add(System.getenv("USERPROFILE"));
163 locations.add(System.getenv("HOME"));
164 locations.remove(null);
165 for (String loc : locations) {
166 File location = new File(loc);
167 configFile.addLocation(new File(location, "Mercurial.ini"));
168 configFile.addLocation(new File(location, ".hgrc"));
169 }
127 } else { 170 } else {
128 // FIXME read from install-root 171 if (hgInstallRoot != null) {
129 // 172 File d = new File(hgInstallRoot, "etc/mercurial/hgrc.d/");
130 File d = new File("/etc/mercurial/hgrc.d/"); 173 if (d.isDirectory() && d.canRead()) {
131 if (d.isDirectory()) { 174 for (File f : listConfigFiles(d)) {
132 for (File f : d.listFiles()) {
133 // XXX in fact, need to sort in alphabetical order
134 if (f.getName().endsWith(".rc")) {
135 configFile.addLocation(f); 175 configFile.addLocation(f);
136 } 176 }
137 } 177 }
178 configFile.addLocation(new File(hgInstallRoot, "etc/mercurial/hgrc"));
179 }
180 // same, but with absolute paths
181 File d = new File("/etc/mercurial/hgrc.d/");
182 if (d.isDirectory() && d.canRead()) {
183 for (File f : listConfigFiles(d)) {
184 configFile.addLocation(f);
185 }
138 } 186 }
139 configFile.addLocation(new File("/etc/mercurial/hgrc")); 187 configFile.addLocation(new File("/etc/mercurial/hgrc"));
140 } 188 configFile.addLocation(new File(System.getenv("HOME"), ".hgrc"));
141 configFile.addLocation(new File(System.getProperty("user.home"), ".hgrc")); 189 }
142 // last one, overrides anything else 190 // last one, overrides anything else
143 // <repo>/.hg/hgrc 191 // <repo>/.hg/hgrc
144 configFile.addLocation(new File(repoRoot, "hgrc")); 192 configFile.addLocation(new File(repoRoot, "hgrc"));
145 return configFile; 193 return configFile;
146 } 194 }
147 195
148 /** 196 private static List<File> getWindowsConfigFilesPerInstall(File hgInstallDir) {
149 * @return 197 File f = new File(hgInstallDir, "Mercurial.ini");
150 */ 198 if (f.exists()) {
199 return Collections.singletonList(f);
200 }
201 f = new File(hgInstallDir, "hgrc.d/");
202 if (f.canRead() && f.isDirectory()) {
203 return listConfigFiles(f);
204 }
205 // FIXME query registry, e.g. with
206 // Runtime.exec("reg query HKLM\Software\Mercurial")
207 //
208 f = new File("C:\\Mercurial\\Mercurial.ini");
209 if (f.exists()) {
210 return Collections.singletonList(f);
211 }
212 return Collections.emptyList();
213 }
214
215 private static List<File> listConfigFiles(File dir) {
216 assert dir.canRead();
217 assert dir.isDirectory();
218 final File[] allFiles = dir.listFiles();
219 // File is Comparable, lexicographically by default
220 Arrays.sort(allFiles);
221 ArrayList<File> rv = new ArrayList<File>(allFiles.length);
222 for (File f : allFiles) {
223 if (f.getName().endsWith(".rc")) {
224 rv.add(f);
225 }
226 }
227 return rv;
228 }
229
151 public static File getInstallationConfigurationFileToWrite() { 230 public static File getInstallationConfigurationFileToWrite() {
152 // TODO Auto-generated method stub 231 File hgInstallRoot = findHgInstallRoot(); // may be null
153 // FIXME find out install-root 232 // choice of which hgrc to pick here is according to my own pure discretion
154 throw new UnsupportedOperationException(); 233 if (hgInstallRoot != null) {
155 } 234 // use this location only if it's writable
156 235 File cfg = new File(hgInstallRoot, runningOnWindows() ? "Mercurial.ini" : "etc/mercurial/hgrc");
157 /** 236 if (cfg.canWrite() || cfg.getParentFile().canWrite()) {
158 * @return 237 return cfg;
159 */ 238 }
239 }
240 // fallback
241 if (runningOnWindows()) {
242 if (hgInstallRoot == null) {
243 return new File("C:\\Mercurial\\Mercurial.ini");
244 } else {
245 // yes, we tried this file already (above) and found it non-writable
246 // let caller fail with can't write
247 return new File(hgInstallRoot, "Mercurial.ini");
248 }
249 } else {
250 return new File("/etc/mercurial/hgrc");
251 }
252 }
253
160 public static File getUserConfigurationFileToWrite() { 254 public static File getUserConfigurationFileToWrite() {
161 final File rv = new File(System.getProperty("user.home"), ".hgrc"); 255 LinkedHashSet<String> locations = new LinkedHashSet<String>();
162 if (rv.exists() && rv.canWrite()) { 256 final boolean runsOnWindows = runningOnWindows();
163 return rv; 257 if (runsOnWindows) {
164 } 258 locations.add(System.getenv("USERPROFILE"));
165 if (runningOnWindows()) { 259 }
166 // try another well-known location 260 locations.add(System.getenv("HOME"));
167 // TODO comment above regarding USERPROFILE and HOME variables applies here as well 261 locations.remove(null);
168 File f = new File(System.getProperty("user.home"), "Mercurial.ini"); 262 for (String loc : locations) {
169 if (f.exists() && f.canWrite()) { 263 File location = new File(loc);
170 return f; 264 File rv = new File(location, ".hgrc");
171 } 265 if (rv.exists() && rv.canWrite()) {
172 } 266 return rv;
173 // fallback to default value 267 }
174 return rv; 268 if (runsOnWindows) {
269 rv = new File(location, "Mercurial.ini");
270 if (rv.exists() && rv.canWrite()) {
271 return rv;
272 }
273 }
274 }
275 // fallback to default, let calling code fail with Exception if can't write
276 return new File(System.getProperty("user.home"), ".hgrc");
175 } 277 }
176 } 278 }