src/share/tools/MakeDeps/Database.java

Sat, 01 Dec 2007 00:00:00 +0000

author
duke
date
Sat, 01 Dec 2007 00:00:00 +0000
changeset 435
a61af66fc99e
child 648
8b48a7bd2bf7
permissions
-rw-r--r--

Initial load

duke@435 1 /*
duke@435 2 * Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved.
duke@435 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
duke@435 4 *
duke@435 5 * This code is free software; you can redistribute it and/or modify it
duke@435 6 * under the terms of the GNU General Public License version 2 only, as
duke@435 7 * published by the Free Software Foundation.
duke@435 8 *
duke@435 9 * This code is distributed in the hope that it will be useful, but WITHOUT
duke@435 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
duke@435 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
duke@435 12 * version 2 for more details (a copy is included in the LICENSE file that
duke@435 13 * accompanied this code).
duke@435 14 *
duke@435 15 * You should have received a copy of the GNU General Public License version
duke@435 16 * 2 along with this work; if not, write to the Free Software Foundation,
duke@435 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
duke@435 18 *
duke@435 19 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
duke@435 20 * CA 95054 USA or visit www.sun.com if you need additional information or
duke@435 21 * have any questions.
duke@435 22 *
duke@435 23 */
duke@435 24
duke@435 25 import java.io.*;
duke@435 26 import java.util.*;
duke@435 27
duke@435 28 public class Database {
duke@435 29 private MacroDefinitions macros;
duke@435 30 // allFiles is kept in lexicographically sorted order. See get().
duke@435 31 private FileList allFiles;
duke@435 32 // files that have implicit dependency on platform files
duke@435 33 // e.g. os.hpp: os_<os_family>.hpp os_<os_arch>.hpp but only
duke@435 34 // recorded if the platform file was seen.
duke@435 35 private FileList platformFiles;
duke@435 36 private FileList outerFiles;
duke@435 37 private FileList indivIncludes;
duke@435 38 private FileList grandInclude; // the results for the grand include file
duke@435 39 private long threshold;
duke@435 40 private int nOuterFiles;
duke@435 41 private int nPrecompiledFiles;
duke@435 42 private boolean missingOk;
duke@435 43 private Platform plat;
duke@435 44 /** These allow you to specify files not in the include database
duke@435 45 which are prepended and appended to the file list, allowing
duke@435 46 you to have well-known functions at the start and end of the
duke@435 47 text segment (allows us to find out in a portable fashion
duke@435 48 whether the current PC is in VM code or not upon a crash) */
duke@435 49 private String firstFile;
duke@435 50 private String lastFile;
duke@435 51
duke@435 52 public Database(Platform plat, long t) {
duke@435 53 this.plat = plat;
duke@435 54 macros = new MacroDefinitions();
duke@435 55 allFiles = new FileList("allFiles", plat);
duke@435 56 platformFiles = new FileList("platformFiles", plat);
duke@435 57 outerFiles = new FileList("outerFiles", plat);
duke@435 58 indivIncludes = new FileList("IndivIncludes", plat);
duke@435 59 grandInclude = new FileList(plat.getGIFileTemplate().nameOfList(), plat);
duke@435 60
duke@435 61 threshold = t;
duke@435 62 nOuterFiles = 0;
duke@435 63 nPrecompiledFiles = 0;
duke@435 64 missingOk = false;
duke@435 65 firstFile = null;
duke@435 66 lastFile = null;
duke@435 67 };
duke@435 68
duke@435 69 public FileList getAllFiles() {
duke@435 70 return allFiles;
duke@435 71 }
duke@435 72
duke@435 73 public Iterator getMacros() {
duke@435 74 return macros.getMacros();
duke@435 75 }
duke@435 76
duke@435 77 public void canBeMissing() {
duke@435 78 missingOk = true;
duke@435 79 }
duke@435 80
duke@435 81 public boolean hfileIsInGrandInclude(FileList hfile, FileList cfile) {
duke@435 82 return ((hfile.getCount() >= threshold) && (cfile.getUseGrandInclude()));
duke@435 83 }
duke@435 84
duke@435 85 /** These allow you to specify files not in the include database
duke@435 86 which are prepended and appended to the file list, allowing
duke@435 87 you to have well-known functions at the start and end of the
duke@435 88 text segment (allows us to find out in a portable fashion
duke@435 89 whether the current PC is in VM code or not upon a crash) */
duke@435 90 public void setFirstFile(String fileName) {
duke@435 91 firstFile = fileName;
duke@435 92 }
duke@435 93
duke@435 94 public void setLastFile(String fileName) {
duke@435 95 lastFile = fileName;
duke@435 96 }
duke@435 97
duke@435 98 public void get(String platFileName, String dbFileName)
duke@435 99 throws FileFormatException, IOException, FileNotFoundException {
duke@435 100 macros.readFrom(platFileName, missingOk);
duke@435 101
duke@435 102 BufferedReader reader = null;
duke@435 103 try {
duke@435 104 reader = new BufferedReader(new FileReader(dbFileName));
duke@435 105 } catch (FileNotFoundException e) {
duke@435 106 if (missingOk) {
duke@435 107 return;
duke@435 108 } else {
duke@435 109 throw(e);
duke@435 110 }
duke@435 111 }
duke@435 112 System.out.println("\treading database: " + dbFileName);
duke@435 113 String line;
duke@435 114 int lineNo = 0;
duke@435 115 do {
duke@435 116 line = reader.readLine();
duke@435 117 lineNo++;
duke@435 118 if (line != null) {
duke@435 119 StreamTokenizer tokenizer =
duke@435 120 new StreamTokenizer(new StringReader(line));
duke@435 121 tokenizer.slashSlashComments(true);
duke@435 122 tokenizer.wordChars('_', '_');
duke@435 123 tokenizer.wordChars('<', '>');
duke@435 124 // NOTE: if we didn't have to do this line by line,
duke@435 125 // we could trivially recognize C-style comments as
duke@435 126 // well.
duke@435 127 // tokenizer.slashStarComments(true);
duke@435 128 int numTok = 0;
duke@435 129 int res;
duke@435 130 String unexpandedIncluder = null;
duke@435 131 String unexpandedIncludee = null;
duke@435 132 do {
duke@435 133 res = tokenizer.nextToken();
duke@435 134 if (res != StreamTokenizer.TT_EOF) {
duke@435 135 if (numTok == 0) {
duke@435 136 unexpandedIncluder = tokenizer.sval;
duke@435 137 } else if (numTok == 1) {
duke@435 138 unexpandedIncludee = tokenizer.sval;
duke@435 139 } else {
duke@435 140 throw new FileFormatException(
duke@435 141 "invalid line: \"" + line +
duke@435 142 "\". Error position: line " + lineNo
duke@435 143 );
duke@435 144 }
duke@435 145 numTok++;
duke@435 146 }
duke@435 147 } while (res != StreamTokenizer.TT_EOF);
duke@435 148
duke@435 149 if ((numTok != 0) && (numTok != 2)) {
duke@435 150 throw new FileFormatException(
duke@435 151 "invalid line: \"" + line +
duke@435 152 "\". Error position: line " + lineNo
duke@435 153 );
duke@435 154 }
duke@435 155
duke@435 156 if (numTok == 2) {
duke@435 157 // Non-empty line
duke@435 158 String includer = macros.expand(unexpandedIncluder);
duke@435 159 String includee = macros.expand(unexpandedIncludee);
duke@435 160
duke@435 161 if (includee.equals(plat.generatePlatformDependentInclude())) {
duke@435 162 MacroDefinitions localExpander = macros.copy();
duke@435 163 MacroDefinitions localExpander2 = macros.copy();
duke@435 164 localExpander.setAllMacroBodiesTo("pd");
duke@435 165 localExpander2.setAllMacroBodiesTo("");
duke@435 166
duke@435 167 // unexpanded_includer e.g. thread_<os_arch>.hpp
duke@435 168 // thread_solaris_i486.hpp -> _thread_pd.hpp.incl
duke@435 169
duke@435 170 FileName pdName =
duke@435 171 plat.getInclFileTemplate().copyStem(
duke@435 172 localExpander.expand(unexpandedIncluder)
duke@435 173 );
duke@435 174
duke@435 175 // derive generic name from platform specific name
duke@435 176 // e.g. os_<arch_os>.hpp => os.hpp. We enforce the
duke@435 177 // restriction (imperfectly) noted in includeDB_core
duke@435 178 // that platform specific files will have an underscore
duke@435 179 // preceding the macro invocation.
duke@435 180
duke@435 181 // First expand macro as null string.
duke@435 182
duke@435 183 String newIncluder_temp =
duke@435 184 localExpander2.expand(unexpandedIncluder);
duke@435 185
duke@435 186 // Now find "_." and remove the underscore.
duke@435 187
duke@435 188 String newIncluder = "";
duke@435 189
duke@435 190 int len = newIncluder_temp.length();
duke@435 191 int count = 0;
duke@435 192
duke@435 193 for ( int i = 0; i < len - 1 ; i++ ) {
duke@435 194 if (newIncluder_temp.charAt(i) == '_' && newIncluder_temp.charAt(i+1) == '.') {
duke@435 195 count++;
duke@435 196 } else {
duke@435 197 newIncluder += newIncluder_temp.charAt(i);
duke@435 198 }
duke@435 199 }
duke@435 200 newIncluder += newIncluder_temp.charAt(len-1);
duke@435 201
duke@435 202 if (count != 1) {
duke@435 203 throw new FileFormatException(
duke@435 204 "Unexpected filename format for platform dependent file.\nline: \"" + line +
duke@435 205 "\".\nError position: line " + lineNo
duke@435 206 );
duke@435 207 }
duke@435 208
duke@435 209 FileList p = allFiles.listForFile(includer);
duke@435 210 p.setPlatformDependentInclude(pdName.dirPreStemSuff());
duke@435 211
duke@435 212 // Add an implicit dependency on platform
duke@435 213 // specific file for the generic file
duke@435 214
duke@435 215 p = platformFiles.listForFile(newIncluder);
duke@435 216
duke@435 217 // if this list is empty then this is 1st
duke@435 218 // occurance of a platform dependent file and
duke@435 219 // we need a new version of the include file.
duke@435 220 // Otherwise we just append to the current
duke@435 221 // file.
duke@435 222
duke@435 223 PrintWriter pdFile =
duke@435 224 new PrintWriter(
duke@435 225 new FileWriter(pdName.dirPreStemSuff(),
duke@435 226 !p.isEmpty())
duke@435 227 );
duke@435 228 pdFile.println("# include \"" + includer + "\"");
duke@435 229 pdFile.close();
duke@435 230
duke@435 231 // Add the platform specific file to the list
duke@435 232 // for this generic file.
duke@435 233
duke@435 234 FileList q = allFiles.listForFile(includer);
duke@435 235 p.addIfAbsent(q);
duke@435 236 } else {
duke@435 237 FileList p = allFiles.listForFile(includer);
duke@435 238 if (isOuterFile(includer))
duke@435 239 outerFiles.addIfAbsent(p);
duke@435 240
duke@435 241 if (includee.equals(plat.noGrandInclude())) {
duke@435 242 p.setUseGrandInclude(false);
duke@435 243 } else {
duke@435 244 FileList q = allFiles.listForFile(includee);
duke@435 245 p.addIfAbsent(q);
duke@435 246 }
duke@435 247 }
duke@435 248 }
duke@435 249 }
duke@435 250 } while (line != null);
duke@435 251 reader.close();
duke@435 252
duke@435 253 // Keep allFiles in well-known order so we can easily determine
duke@435 254 // whether the known files are the same
duke@435 255 allFiles.sortByName();
duke@435 256
duke@435 257 // Add first and last files differently to prevent a mistake
duke@435 258 // in ordering in the include databases from breaking the
duke@435 259 // error reporting in the VM.
duke@435 260 if (firstFile != null) {
duke@435 261 FileList p = allFiles.listForFile(firstFile);
duke@435 262 allFiles.setFirstFile(p);
duke@435 263 outerFiles.setFirstFile(p);
duke@435 264 }
duke@435 265
duke@435 266 if (lastFile != null) {
duke@435 267 FileList p = allFiles.listForFile(lastFile);
duke@435 268 allFiles.setLastFile(p);
duke@435 269 outerFiles.setLastFile(p);
duke@435 270 }
duke@435 271 }
duke@435 272
duke@435 273 public void compute() {
duke@435 274 System.out.println("\tcomputing closures\n");
duke@435 275 // build both indiv and grand results
duke@435 276 for (Iterator iter = outerFiles.iterator(); iter.hasNext(); ) {
duke@435 277 indivIncludes.add(((FileList) iter.next()).doCFile());
duke@435 278 ++nOuterFiles;
duke@435 279 }
duke@435 280
duke@435 281 if (!plat.haveGrandInclude())
duke@435 282 return; // nothing in grand include
duke@435 283
duke@435 284 // count how many times each include is included & add em to grand
duke@435 285 for (Iterator iter = indivIncludes.iterator(); iter.hasNext(); ) {
duke@435 286 FileList indivInclude = (FileList) iter.next();
duke@435 287 if (!indivInclude.getUseGrandInclude()) {
duke@435 288 continue; // do not bump count if my files cannot be
duke@435 289 // in grand include
duke@435 290 }
duke@435 291 indivInclude.doFiles(grandInclude); // put em on
duke@435 292 // grand_include list
duke@435 293 for (Iterator incListIter = indivInclude.iterator();
duke@435 294 incListIter.hasNext(); ) {
duke@435 295 ((FileList) incListIter.next()).incrementCount();
duke@435 296 }
duke@435 297 }
duke@435 298 }
duke@435 299
duke@435 300 // Not sure this is necessary in Java
duke@435 301 public void verify() {
duke@435 302 for (Iterator iter = indivIncludes.iterator(); iter.hasNext(); ) {
duke@435 303 if (iter.next() == null) {
duke@435 304 plat.abort();
duke@435 305 }
duke@435 306 }
duke@435 307 }
duke@435 308
duke@435 309 public void put() throws IOException {
duke@435 310 writeIndividualIncludes();
duke@435 311
duke@435 312 if (plat.haveGrandInclude())
duke@435 313 writeGrandInclude();
duke@435 314
duke@435 315 writeGrandUnixMakefile();
duke@435 316 }
duke@435 317
duke@435 318 private void writeIndividualIncludes() throws IOException {
duke@435 319 System.out.println("\twriting individual include files\n");
duke@435 320
duke@435 321 for (Iterator iter = indivIncludes.iterator(); iter.hasNext(); ) {
duke@435 322 FileList list = (FileList) iter.next();
duke@435 323 System.out.println("\tcreating " + list.getName());
duke@435 324 list.putInclFile(this);
duke@435 325 }
duke@435 326 }
duke@435 327
duke@435 328 private void writeGrandInclude() throws IOException {
duke@435 329 System.out.println("\twriting grand include file\n");
duke@435 330 PrintWriter inclFile =
duke@435 331 new PrintWriter(new FileWriter(plat.getGIFileTemplate().dirPreStemSuff()));
duke@435 332 plat.writeGIPragma(inclFile);
duke@435 333 for (Iterator iter = grandInclude.iterator(); iter.hasNext(); ) {
duke@435 334 FileList list = (FileList) iter.next();
duke@435 335 if (list.getCount() >= threshold) {
duke@435 336 inclFile.println("# include \"" +
duke@435 337 plat.getGIFileTemplate().getInvDir() +
duke@435 338 list.getName() +
duke@435 339 "\"");
duke@435 340 nPrecompiledFiles += 1;
duke@435 341 }
duke@435 342 }
duke@435 343 inclFile.println();
duke@435 344 inclFile.close();
duke@435 345 }
duke@435 346
duke@435 347 private void writeGrandUnixMakefile() throws IOException {
duke@435 348 if (!plat.writeDeps())
duke@435 349 return;
duke@435 350
duke@435 351 System.out.println("\twriting dependencies file\n");
duke@435 352 PrintWriter gd =
duke@435 353 new PrintWriter(new FileWriter(
duke@435 354 plat.getGDFileTemplate().dirPreStemSuff())
duke@435 355 );
duke@435 356 gd.println("# generated by makeDeps");
duke@435 357 gd.println();
duke@435 358
duke@435 359
duke@435 360 // HACK ALERT. The compilation of ad_<arch> files is very slow.
duke@435 361 // We want to start compiling them as early as possible. The compilation
duke@435 362 // order on unix is dependant on the order we emit files here.
duke@435 363 // By sorting the output before emitting it, we expect
duke@435 364 // that ad_<arch> will be compiled early.
duke@435 365 boolean shouldSortObjFiles = true;
duke@435 366
duke@435 367 if (shouldSortObjFiles) {
duke@435 368 ArrayList sortList = new ArrayList();
duke@435 369
duke@435 370 // We need to preserve the ordering of the first and last items
duke@435 371 // in outerFiles.
duke@435 372 int size = outerFiles.size() - 1;
duke@435 373 String firstName = removeSuffixFrom(((FileList)outerFiles.get(0)).getName());
duke@435 374 String lastName = removeSuffixFrom(((FileList)outerFiles.get(size)).getName());
duke@435 375
duke@435 376 for (int i=1; i<size; i++) {
duke@435 377 FileList anOuterFile = (FileList)outerFiles.get(i);
duke@435 378 String stemName = removeSuffixFrom(anOuterFile.getName());
duke@435 379 sortList.add(stemName);
duke@435 380 }
duke@435 381 Collections.sort(sortList);
duke@435 382
duke@435 383 // write Obj_Files = ...
duke@435 384 gd.println("Obj_Files = \\");
duke@435 385 gd.println(firstName + plat.objFileSuffix() + " \\");
duke@435 386 for (Iterator iter = sortList.iterator(); iter.hasNext(); ) {
duke@435 387 gd.println(iter.next() + plat.objFileSuffix() + " \\");
duke@435 388 }
duke@435 389 gd.println(lastName + plat.objFileSuffix() + " \\");
duke@435 390 gd.println();
duke@435 391 gd.println();
duke@435 392 } else {
duke@435 393 // write Obj_Files = ...
duke@435 394 gd.println("Obj_Files = \\");
duke@435 395 for (Iterator iter = outerFiles.iterator(); iter.hasNext(); ) {
duke@435 396 FileList anOuterFile = (FileList) iter.next();
duke@435 397
duke@435 398 String stemName = removeSuffixFrom(anOuterFile.getName());
duke@435 399 gd.println(stemName + plat.objFileSuffix() + " \\");
duke@435 400 }
duke@435 401 gd.println();
duke@435 402 gd.println();
duke@435 403 }
duke@435 404
duke@435 405 if (nPrecompiledFiles > 0) {
duke@435 406 // write Precompiled_Files = ...
duke@435 407 gd.println("Precompiled_Files = \\");
duke@435 408 for (Iterator iter = grandInclude.iterator(); iter.hasNext(); ) {
duke@435 409 FileList list = (FileList) iter.next();
duke@435 410 gd.println(list.getName() + " \\");
duke@435 411 }
duke@435 412 gd.println();
duke@435 413 gd.println();
duke@435 414 }
duke@435 415
duke@435 416 gd.println("DTraced_Files = \\");
duke@435 417 for (Iterator iter = outerFiles.iterator(); iter.hasNext(); ) {
duke@435 418 FileList anOuterFile = (FileList) iter.next();
duke@435 419
duke@435 420 if (anOuterFile.hasListForFile("dtrace.hpp")) {
duke@435 421 String stemName = removeSuffixFrom(anOuterFile.getName());
duke@435 422 gd.println(stemName + plat.objFileSuffix() + " \\");
duke@435 423 }
duke@435 424 }
duke@435 425 gd.println();
duke@435 426 gd.println();
duke@435 427
duke@435 428 {
duke@435 429 // write each dependency
duke@435 430
duke@435 431 for (Iterator iter = indivIncludes.iterator(); iter.hasNext(); ) {
duke@435 432
duke@435 433 FileList anII = (FileList) iter.next();
duke@435 434
duke@435 435 String stemName = removeSuffixFrom(anII.getName());
duke@435 436 String inclFileName =
duke@435 437 plat.getInclFileTemplate().copyStem(anII.getName()).
duke@435 438 preStemSuff();
duke@435 439
duke@435 440 gd.println(stemName + plat.objFileSuffix() + " " +
duke@435 441 stemName + plat.asmFileSuffix() + ": \\");
duke@435 442
duke@435 443 printDependentOn(gd, anII.getName());
duke@435 444 // this gets the include file that includes all that
duke@435 445 // this file needs (first level) since nested includes
duke@435 446 // are skipped to avoid cycles.
duke@435 447 printDependentOn(gd, inclFileName);
duke@435 448
duke@435 449 if ( plat.haveGrandInclude() ) {
duke@435 450 printDependentOn(gd,
duke@435 451 plat.getGIFileTemplate().preStemSuff());
duke@435 452 }
duke@435 453
duke@435 454 for (Iterator iiIter = anII.iterator(); iiIter.hasNext(); ) {
duke@435 455 FileList hfile = (FileList) iiIter.next();
duke@435 456 if (!hfileIsInGrandInclude(hfile, anII) ||
duke@435 457 plat.writeDependenciesOnHFilesFromGI()) {
duke@435 458 printDependentOn(gd, hfile.getName());
duke@435 459 }
duke@435 460 if (platformFiles.hasListForFile(hfile.getName())) {
duke@435 461 FileList p =
duke@435 462 platformFiles.listForFile(hfile.getName());;
duke@435 463 for (Iterator hiIter = p.iterator();
duke@435 464 hiIter.hasNext(); ) {
duke@435 465 FileList hi2 = (FileList) hiIter.next();
duke@435 466 if (!hfileIsInGrandInclude(hi2, p)) {
duke@435 467 printDependentOn(gd, hi2.getName());
duke@435 468 }
duke@435 469 }
duke@435 470 }
duke@435 471 }
duke@435 472
duke@435 473 if (plat.includeGIDependencies()
duke@435 474 && nPrecompiledFiles > 0
duke@435 475 && anII.getUseGrandInclude()) {
duke@435 476 gd.println(" $(Precompiled_Files) \\");
duke@435 477 }
duke@435 478 gd.println();
duke@435 479 gd.println();
duke@435 480 }
duke@435 481 }
duke@435 482
duke@435 483 gd.close();
duke@435 484 }
duke@435 485
duke@435 486 public void putDiffs(Database previous) throws IOException {
duke@435 487 System.out.println("\tupdating output files\n");
duke@435 488
duke@435 489 if (!indivIncludes.compareLists(previous.indivIncludes)
duke@435 490 || !grandInclude.compareLists(previous.grandInclude)) {
duke@435 491 System.out.println("The order of .c or .s has changed, or " +
duke@435 492 "the grand include file has changed.");
duke@435 493 put();
duke@435 494 return;
duke@435 495 }
duke@435 496
duke@435 497 Iterator curIter = indivIncludes.iterator();
duke@435 498 Iterator prevIter = previous.indivIncludes.iterator();
duke@435 499
duke@435 500 try {
duke@435 501 while (curIter.hasNext()) {
duke@435 502 FileList newCFileList = (FileList) curIter.next();
duke@435 503 FileList prevCFileList = (FileList) prevIter.next();
duke@435 504 if (!newCFileList.compareLists(prevCFileList)) {
duke@435 505 System.out.println("\tupdating " + newCFileList.getName());
duke@435 506 newCFileList.putInclFile(this);
duke@435 507 }
duke@435 508 }
duke@435 509 }
duke@435 510 catch (Exception e) {
duke@435 511 throw new InternalError("assertion failure: cur and prev " +
duke@435 512 "database lists changed unexpectedly.");
duke@435 513 }
duke@435 514
duke@435 515 writeGrandUnixMakefile();
duke@435 516 }
duke@435 517
duke@435 518 private void printDependentOn(PrintWriter gd, String name) {
duke@435 519 gd.print(" ");
duke@435 520 gd.print(plat.dependentPrefix() + name);
duke@435 521 }
duke@435 522
duke@435 523 private boolean isOuterFile(String s) {
duke@435 524 int len = s.length();
duke@435 525 String[] suffixes = plat.outerSuffixes();
duke@435 526 for (int i = 0; i < suffixes.length; i++) {
duke@435 527 String suffix = suffixes[i];
duke@435 528 int suffLen = suffix.length();
duke@435 529 if ((len >= suffLen) &&
duke@435 530 (plat.fileNameStringEquality(s.substring(len - suffLen),
duke@435 531 suffix))) {
duke@435 532 return true;
duke@435 533 }
duke@435 534 }
duke@435 535 return false;
duke@435 536 }
duke@435 537
duke@435 538 private String removeSuffixFrom(String s) {
duke@435 539 int idx = s.lastIndexOf('.');
duke@435 540 if (idx <= 0)
duke@435 541 plat.abort();
duke@435 542 return s.substring(0, idx);
duke@435 543 }
duke@435 544 }

mercurial