### Eclipse Workspace Patch 1.0 #P org.eclipse.jdt.core.tests.compiler Index: src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java,v retrieving revision 1.59.2.1 diff -u -r1.59.2.1 BatchCompilerTest.java --- src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java 17 May 2006 17:11:27 -0000 1.59.2.1 +++ src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java 26 May 2006 10:52:02 -0000 @@ -2914,8 +2914,88 @@ + " -1.5 -g -preserveAllLocals" + " -d \"" + OUTPUT_DIR + File.separator + "X.java\"", "", - "No .class file created for file X.class in ---OUTPUT_DIR_PLACEHOLDER---/X.java because of an IOException: The output directory is a file : ---OUTPUT_DIR_PLACEHOLDER---/X.java\n", - true); + "No .class file created for file X.class in ---OUTPUT_DIR_PLACEHOLDER" + + "---/X.java because of an IOException: Regular file " + + "---OUTPUT_DIR_PLACEHOLDER---/X.java cannot be used " + + "as output directory\n", + true); +} +// suggested by https://bugs.eclipse.org/bugs/show_bug.cgi?id=141522 +// only checking messages (the bug itself involves concurrent access to +// the file system and a true test case would call for instrumented +// code) +public void test054(){ + this.runConformTest( + new String[] { + "X.java", + "public class X {}", + "f", // create simple file f + "" + }, + "\"" + OUTPUT_DIR + File.separator + "X.java\"" + + " -1.5 -g -preserveAllLocals" + + " -d \"" + OUTPUT_DIR + "/f/out\"", + "", + "No .class file created for file X.class in ---OUTPUT_DIR_PLACEHOLDER" + + "---/f/out because of an IOException: " + + "Could not create output directory ---OUTPUT_DIR_PLACEHOLDER---/f/out\n", + true); +} +// suggested by https://bugs.eclipse.org/bugs/show_bug.cgi?id=141522 +// only checking messages (the bug itself involves concurrent access to +// the file system and a true test case would call for instrumented +// code) +// this test only works on appropriate file systems +public void test055(){ + if (File.separatorChar == '/') { + String tentativeOutputDirNameTail = + File.separator + "out"; + File outputDirectory = new File(OUTPUT_DIR + tentativeOutputDirNameTail); + outputDirectory.mkdirs(); + outputDirectory.setReadOnly(); + // read-only directories do not prevent file creation + // on under-gifted file systems + this.runConformTest( + new String[] { + "p/X.java", + "package p;\n" + + "public class X {}", + }, + "\"" + OUTPUT_DIR + File.separator + "p/X.java\"" + + " -1.5 -g -preserveAllLocals" + + " -d \"" + OUTPUT_DIR + "/out\"", + "", + "No .class file created for file p/X.class in " + + "---OUTPUT_DIR_PLACEHOLDER---/out because of " + + "an IOException: Could not create subdirectory p into output directory " + + "---OUTPUT_DIR_PLACEHOLDER---/out\n", + false /* do not flush output directory */); + } +} +// suggested by https://bugs.eclipse.org/bugs/show_bug.cgi?id=141522 +// only checking messages (the bug itself involves concurrent access to +// the file system and a true test case would call for instrumented +// code) +public void test056(){ + String tentativeOutputDirNameTail = + File.separator + "out"; + this.runConformTest( + new String[] { + "p/X.java", + "package p;\n" + + "public class X {}", + "out/p", // create simple file out/p + "" + }, + "\"" + OUTPUT_DIR + File.separator + "p/X.java\"" + + " -1.5 -g -preserveAllLocals" + + " -d \"" + OUTPUT_DIR + tentativeOutputDirNameTail + "\"", + "", + "No .class file created for file p/X.class in " + + "---OUTPUT_DIR_PLACEHOLDER---/out" + + " because of an IOException: Regular file ---OUTPUT_DIR_PLACEHOLDER---" + + "/out/p cannot be used as output directory\n", + true); } public static Class testClass() { return BatchCompilerTest.class; #P org.eclipse.jdt.core Index: compiler/org/eclipse/jdt/internal/compiler/messages.properties =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/messages.properties,v retrieving revision 1.6 diff -u -r1.6 messages.properties --- compiler/org/eclipse/jdt/internal/compiler/messages.properties 18 Apr 2006 19:08:15 -0000 1.6 +++ compiler/org/eclipse/jdt/internal/compiler/messages.properties 26 May 2006 10:52:05 -0000 @@ -23,9 +23,9 @@ compilation_internalError = Internal compiler error ### output -output_isFile = The output directory is a file : {0} -output_notValidAll = The output directory {0} is not a valid directory name. All the directories cannot be created -output_notValid = The output directory ''{0}'' is not a valid directory name. The directory cannot be created +output_isFile = Regular file {0} cannot be used as output directory +output_notValidAll = Could not create output directory {0} +output_notValid = Could not create subdirectory {0} into output directory {1} ### problem problem_noSourceInformation = Index: compiler/org/eclipse/jdt/internal/compiler/ClassFile.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ClassFile.java,v retrieving revision 1.137 diff -u -r1.137 ClassFile.java --- compiler/org/eclipse/jdt/internal/compiler/ClassFile.java 18 Apr 2006 19:08:15 -0000 1.137 +++ compiler/org/eclipse/jdt/internal/compiler/ClassFile.java 26 May 2006 10:52:05 -0000 @@ -110,45 +110,102 @@ char fileSeparatorChar = File.separatorChar; String fileSeparator = File.separator; File f; - // First we ensure that the outputPath exists outputPath = outputPath.replace('/', fileSeparatorChar); - // To be able to pass the mkdirs() method we need to remove the extra file separator at the end of the outDir name - if (outputPath.endsWith(fileSeparator)) { - outputPath = outputPath.substring(0, outputPath.length() - 1); - } - f = new File(outputPath); - if (f.exists()) { - if (!f.isDirectory()) { - final String message = Messages.bind(Messages.output_isFile, f.getAbsolutePath()); - throw new IOException(message); + // these could be optimized out if we normalized paths once and for + // all + relativeFileName = relativeFileName.replace('/', fileSeparatorChar); + String outputDirPath, fileName; + int separatorIndex = relativeFileName.lastIndexOf(fileSeparatorChar); + if (separatorIndex == -1) { + if (outputPath.endsWith(fileSeparator)) { + outputDirPath = outputPath.substring(0, outputPath.length() - 1); + fileName = outputPath + relativeFileName; + } else { + outputDirPath = outputPath; + fileName = outputPath + fileSeparator + relativeFileName; } } else { - // we have to create that directory - if (!f.mkdirs()) { - final String message = Messages.bind(Messages.output_notValidAll, f.getAbsolutePath()); - throw new IOException(message); + if (outputPath.endsWith(fileSeparator)) { + outputDirPath = outputPath + + relativeFileName.substring(0, separatorIndex); + fileName = outputPath + relativeFileName; + } else { + outputDirPath = outputPath + fileSeparator + + relativeFileName.substring(0, separatorIndex); + fileName = outputPath + fileSeparator + relativeFileName; } } - StringBuffer outDir = new StringBuffer(outputPath); - outDir.append(fileSeparator); - StringTokenizer tokenizer = - new StringTokenizer(relativeFileName, fileSeparator); - String token = tokenizer.nextToken(); - while (tokenizer.hasMoreTokens()) { - f = new File(outDir.append(token).append(fileSeparator).toString()); + f = new File(outputDirPath); + f.mkdirs(); + if (f.isDirectory()) { + return fileName; + } else { + // the directory creation failed for some reason - retry using + // a slower algorithm so as to refine the diagnostic + if (outputPath.endsWith(fileSeparator)) { + outputPath = outputPath.substring(0, outputPath.length() - 1); + } + f = new File(outputPath); + boolean checkFileType = false; if (f.exists()) { - // The outDir already exists, so we proceed the next entry - // System.out.println("outDir: " + outDir + " already exists."); + checkFileType = true; // pre-existed } else { - // Need to add the outDir - if (!f.mkdir()) { - throw new IOException(Messages.bind(Messages.output_notValid, f.getName())); - } + // we have to create that directory + if (!f.mkdirs()) { + if (f.exists()) { + // someone else created f -- need to check its type + checkFileType = true; + } else { + // no one could create f -- complain + throw new IOException(Messages.bind( + Messages.output_notValidAll, f.getAbsolutePath())); + } + } + } + if (checkFileType) { + if (!f.isDirectory()) { + throw new IOException(Messages.bind( + Messages.output_isFile, f.getAbsolutePath())); + } + } + StringBuffer outDir = new StringBuffer(outputPath); + outDir.append(fileSeparator); + StringTokenizer tokenizer = + new StringTokenizer(relativeFileName, fileSeparator); + String token = tokenizer.nextToken(); + while (tokenizer.hasMoreTokens()) { + f = new File(outDir.append(token).append(fileSeparator).toString()); + checkFileType = false; // reset + if (f.exists()) { + checkFileType = true; // this is suboptimal, but it catches corner cases + // in which a regular file pre-exists + } else { + // we have to create that directory + if (!f.mkdir()) { + if (f.exists()) { + // someone else created f -- need to check its type + checkFileType = true; + } else { + // no one could create f -- complain + throw new IOException(Messages.bind( + Messages.output_notValid, + outDir.substring(outputPath.length() + 1, + outDir.length() - 1), + outputPath)); + } + } + } + if (checkFileType) { + if (!f.isDirectory()) { + throw new IOException(Messages.bind( + Messages.output_isFile, f.getAbsolutePath())); + } + } + token = tokenizer.nextToken(); } - token = tokenizer.nextToken(); + // token contains the last one + return outDir.append(token).toString(); } - // token contains the last one - return outDir.append(token).toString(); } /**