OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 import 'dart:io'; |
| 6 |
| 7 import 'package:path/path.dart'; |
| 8 |
| 9 /** |
| 10 * Type of functions used to compute the contents of a set of generated files. |
| 11 * [pkgPath] is the path to the current package. |
| 12 */ |
| 13 typedef Map<String, FileContentsComputer> DirectoryContentsComputer( |
| 14 String pkgPath); |
| 15 |
| 16 /** |
| 17 * Type of functions used to compute the contents of a generated file. |
| 18 * [pkgPath] is the path to the current package. |
| 19 */ |
| 20 typedef String FileContentsComputer(String pkgPath); |
| 21 |
| 22 /** |
| 23 * Abstract base class representing behaviors common to generated files and |
| 24 * generated directories. |
| 25 */ |
| 26 abstract class GeneratedContent { |
| 27 /** |
| 28 * Check whether the [output] has the correct contents, and return true if it |
| 29 * does. [pkgPath] is the path to the current package. |
| 30 */ |
| 31 bool check(String pkgPath); |
| 32 |
| 33 /** |
| 34 * Replace the [output] with the correct contents. [pkgPath] is the path to |
| 35 * the current package. |
| 36 */ |
| 37 void generate(String pkgPath); |
| 38 |
| 39 /** |
| 40 * Get a [FileSystemEntity] representing the output file or directory. |
| 41 * [pkgPath] is the path to the current package. |
| 42 */ |
| 43 FileSystemEntity output(String pkgPath); |
| 44 |
| 45 /** |
| 46 * Check that all of the [targets] are up to date. If they are not, print |
| 47 * out a message instructing the user to regenerate them, and exit with a |
| 48 * nonzero error code. |
| 49 * |
| 50 * [pkgPath] is the path to the current package. [generatorRelPath] is the |
| 51 * path to a .dart script the user may use to regenerate the targets. |
| 52 * |
| 53 * To avoid mistakes when run on Windows, [generatorRelPath] always uses |
| 54 * POSIX directory separators. |
| 55 */ |
| 56 static void checkAll(String pkgPath, String generatorRelPath, |
| 57 Iterable<GeneratedContent> targets) { |
| 58 bool generateNeeded = false; |
| 59 for (GeneratedContent target in targets) { |
| 60 if (!target.check(pkgPath)) { |
| 61 print( |
| 62 '${target.output(pkgPath).absolute} does not have expected contents.
'); |
| 63 generateNeeded = true; |
| 64 } |
| 65 } |
| 66 if (generateNeeded) { |
| 67 print('Please regenerate using:'); |
| 68 String executable = Platform.executable; |
| 69 String packageRoot = ''; |
| 70 if (Platform.packageRoot != null) { |
| 71 packageRoot = ' --package-root=${Platform.packageRoot}'; |
| 72 } |
| 73 String generateScript = |
| 74 join(pkgPath, joinAll(posix.split(generatorRelPath))); |
| 75 print(' $executable$packageRoot $generateScript'); |
| 76 exit(1); |
| 77 } else { |
| 78 print('All generated files up to date.'); |
| 79 } |
| 80 } |
| 81 |
| 82 /** |
| 83 * Regenerate all of the [targets]. [pkgPath] is the path to the current |
| 84 * package. |
| 85 */ |
| 86 static void generateAll(String pkgPath, Iterable<GeneratedContent> targets) { |
| 87 for (GeneratedContent target in targets) { |
| 88 target.generate(pkgPath); |
| 89 } |
| 90 } |
| 91 } |
| 92 |
| 93 /** |
| 94 * Class representing a single output directory (either generated code or |
| 95 * generated HTML). No other content should exist in the directory. |
| 96 */ |
| 97 class GeneratedDirectory extends GeneratedContent { |
| 98 /** |
| 99 * The path to the directory that will have the generated content. |
| 100 */ |
| 101 final String outputDirPath; |
| 102 |
| 103 /** |
| 104 * Callback function that computes the directory contents. |
| 105 */ |
| 106 final DirectoryContentsComputer directoryContentsComputer; |
| 107 |
| 108 GeneratedDirectory(this.outputDirPath, this.directoryContentsComputer); |
| 109 |
| 110 @override |
| 111 bool check(String pkgPath) { |
| 112 Directory outputDirectory = output(pkgPath); |
| 113 Map<String, FileContentsComputer> map = directoryContentsComputer(pkgPath); |
| 114 try { |
| 115 for (String file in map.keys) { |
| 116 FileContentsComputer fileContentsComputer = map[file]; |
| 117 String expectedContents = fileContentsComputer(pkgPath); |
| 118 File outputFile = new File(posix.join(outputDirectory.path, file)); |
| 119 String actualContents = outputFile.readAsStringSync(); |
| 120 // Normalize Windows line endings to Unix line endings so that the |
| 121 // comparison doesn't fail on Windows. |
| 122 actualContents = actualContents.replaceAll('\r\n', '\n'); |
| 123 if (expectedContents != actualContents) { |
| 124 return false; |
| 125 } |
| 126 } |
| 127 int nonHiddenFileCount = 0; |
| 128 outputDirectory |
| 129 .listSync(recursive: false, followLinks: false) |
| 130 .forEach((FileSystemEntity fileSystemEntity) { |
| 131 if (fileSystemEntity is File && |
| 132 !basename(fileSystemEntity.path).startsWith('.')) { |
| 133 nonHiddenFileCount++; |
| 134 } |
| 135 }); |
| 136 if (nonHiddenFileCount != map.length) { |
| 137 // The number of files generated doesn't match the number we expected to |
| 138 // generate. |
| 139 return false; |
| 140 } |
| 141 } catch (e) { |
| 142 // There was a problem reading the file (most likely because it didn't |
| 143 // exist). Treat that the same as if the file doesn't have the expected |
| 144 // contents. |
| 145 return false; |
| 146 } |
| 147 return true; |
| 148 } |
| 149 |
| 150 @override |
| 151 void generate(String pkgPath) { |
| 152 Directory outputDirectory = output(pkgPath); |
| 153 try { |
| 154 // delete the contents of the directory (and the directory itself) |
| 155 outputDirectory.deleteSync(recursive: true); |
| 156 } catch (e) { |
| 157 // Error caught while trying to delete the directory, this can happen if |
| 158 // it didn't yet exist. |
| 159 } |
| 160 // re-create the empty directory |
| 161 outputDirectory.createSync(recursive: true); |
| 162 |
| 163 // generate all of the files in the directory |
| 164 Map<String, FileContentsComputer> map = directoryContentsComputer(pkgPath); |
| 165 map.forEach((String file, FileContentsComputer fileContentsComputer) { |
| 166 File outputFile = new File(posix.join(outputDirectory.path, file)); |
| 167 outputFile.writeAsStringSync(fileContentsComputer(pkgPath)); |
| 168 }); |
| 169 } |
| 170 |
| 171 @override |
| 172 Directory output(String pkgPath) => |
| 173 new Directory(join(pkgPath, joinAll(posix.split(outputDirPath)))); |
| 174 } |
| 175 |
| 176 /** |
| 177 * Class representing a single output file (either generated code or generated |
| 178 * HTML). |
| 179 */ |
| 180 class GeneratedFile extends GeneratedContent { |
| 181 /** |
| 182 * The output file to which generated output should be written, relative to |
| 183 * the "tool/spec" directory. This filename uses the posix path separator |
| 184 * ('/') regardless of the OS. |
| 185 */ |
| 186 final String outputPath; |
| 187 |
| 188 /** |
| 189 * Callback function which computes the file. |
| 190 */ |
| 191 final FileContentsComputer computeContents; |
| 192 |
| 193 GeneratedFile(this.outputPath, this.computeContents); |
| 194 |
| 195 @override |
| 196 bool check(String pkgPath) { |
| 197 File outputFile = output(pkgPath); |
| 198 String expectedContents = computeContents(pkgPath); |
| 199 try { |
| 200 String actualContents = outputFile.readAsStringSync(); |
| 201 // Normalize Windows line endings to Unix line endings so that the |
| 202 // comparison doesn't fail on Windows. |
| 203 actualContents = actualContents.replaceAll('\r\n', '\n'); |
| 204 return expectedContents == actualContents; |
| 205 } catch (e) { |
| 206 // There was a problem reading the file (most likely because it didn't |
| 207 // exist). Treat that the same as if the file doesn't have the expected |
| 208 // contents. |
| 209 return false; |
| 210 } |
| 211 } |
| 212 |
| 213 @override |
| 214 void generate(String pkgPath) { |
| 215 output(pkgPath).writeAsStringSync(computeContents(pkgPath)); |
| 216 } |
| 217 |
| 218 @override |
| 219 File output(String pkgPath) => |
| 220 new File(join(pkgPath, joinAll(posix.split(outputPath)))); |
| 221 } |
OLD | NEW |