|
16 | 16 | * These are shared settings common to all Google Ads Java library subprojects. |
17 | 17 | */ |
18 | 18 |
|
| 19 | +import org.gradle.api.DefaultTask |
| 20 | +import groovy.io.FileType |
| 21 | +import groovy.io.FileVisitResult |
| 22 | +import java.util.regex.Matcher |
| 23 | + |
19 | 24 | plugins { |
20 | 25 | id 'java-library' |
21 | 26 | id 'maven-publish' |
@@ -58,67 +63,212 @@ dependencies { |
58 | 63 | testImplementation 'junit:junit:4.13.1' |
59 | 64 | } |
60 | 65 |
|
61 | | -public class ExampleRunnerTask extends JavaExec { |
| 66 | +class ExampleRunnerTask extends JavaExec { |
62 | 67 |
|
63 | | - @Input |
64 | | - public String basePackage = 'com.google.ads.googleads.examples.' |
| 68 | + // The base package for all examples. |
| 69 | + private String basePackage = 'com.google.ads.googleads.examples.' |
65 | 70 |
|
| 71 | + // Hints for failed executions. |
| 72 | + private final String exampleInvocation = 'For example, ' + |
| 73 | + '\033[0;35m--example="basicoperations.GetCampaigns"\033[0m.\n' + |
| 74 | + 'Run \033[0;35m./gradlew -q listExamples\033[0m to list all examples, or ' + |
| 75 | + '\033[0;35m./gradlew -q listExamples --subdirectory="basicoperations"\033[0m ' + |
| 76 | + 'to list examples in a specific subdirectory.\n\n' |
| 77 | + |
| 78 | + // The package.ClassName and any arguments required to launch an example, |
| 79 | + // for example "basicoperations.GetCampaigns --customerId 1234567890". |
66 | 80 | @Optional |
67 | 81 | @Input |
68 | 82 | private String exampleArguments |
69 | 83 |
|
70 | | - public ExampleRunnerTask() { |
| 84 | + ExampleRunnerTask() { |
71 | 85 | group = 'Execution' |
72 | 86 | description = 'Run a Google Ads API example.' |
73 | 87 | errorOutput = System.err |
74 | 88 | } |
75 | 89 |
|
| 90 | + String getBasePackage() { |
| 91 | + return this.basePackage |
| 92 | + } |
| 93 | + |
| 94 | + void setBasePackage(String basePackage) { |
| 95 | + this.basePackage = basePackage |
| 96 | + } |
| 97 | + |
| 98 | + String getExampleArguments() { |
| 99 | + return this.exampleArguments |
| 100 | + } |
| 101 | + |
76 | 102 | @Option(option = 'example', description = 'Sets the example to launch and' + |
77 | | - ' any arguments. Required for execution. E.g. ' + |
78 | | - '"basicoperations.GetCampaigns --customerId 1234567890"') |
79 | | - public void setExampleArguments(String exampleArguments) { |
| 103 | + ' any arguments. Required for execution. For example, ' + |
| 104 | + '"basicoperations.GetCampaigns --customerId 1234567890"') |
| 105 | + void setExampleArguments(String exampleArguments) { |
80 | 106 | this.exampleArguments = exampleArguments.trim() |
81 | 107 | int firstSpaceIndex = this.exampleArguments.indexOf(' ') |
82 | 108 |
|
83 | 109 | // No additional arguments were passed, just the example name. |
84 | 110 | if (firstSpaceIndex == -1) { |
85 | | - main = basePackage + this.exampleArguments |
| 111 | + main = this.basePackage + this.exampleArguments |
86 | 112 | } |
87 | 113 | // Otherwise, separate the input and set the arguments to pass to the |
88 | 114 | // main class. |
89 | 115 | else { |
90 | | - main = basePackage + this.exampleArguments[0..firstSpaceIndex - 1] |
| 116 | + main = this.basePackage + this.exampleArguments[0..firstSpaceIndex - 1] |
91 | 117 | argsString(exampleArguments[(firstSpaceIndex + 1)..-1]) |
92 | 118 | } |
93 | 119 | } |
94 | 120 |
|
95 | 121 | @TaskAction |
96 | 122 | @Override |
97 | | - public void exec() { |
| 123 | + void exec() { |
98 | 124 | if (!(exampleArguments?.trim())) { |
99 | 125 | throw new GradleException('\033[0;31mMissing example!\033[0m ' + |
100 | | - 'Please rerun with one provided, e.g. ' + |
101 | | - '\033[0;35m--example="basicoperations.GetCampaigns"\033[0m') |
| 126 | + 'Please rerun with one provided. ' + exampleInvocation) |
102 | 127 | } |
103 | 128 | logQuietMessage('Running example: ' + main + ', args: ' + |
104 | | - args.toString()) |
| 129 | + args.toString()) |
105 | 130 | try { |
106 | 131 | super.exec() |
107 | 132 | } |
108 | 133 | catch (Exception e) { |
109 | 134 | logQuietMessage('\n\033[0;31mrunExample exception!\033[0m Did ' + |
110 | | - 'you provide a valid example identifier? E.g. ' + |
111 | | - '\033[0;35m--example="basicoperations.GetCampaigns"\033[0m\n\n' + |
112 | | - e.message) |
| 135 | + 'you provide a valid example identifier? ' + exampleInvocation + |
| 136 | + e.message) |
113 | 137 | } |
114 | 138 | } |
115 | 139 |
|
116 | | - public String getExampleArguments() { |
117 | | - return exampleArguments |
118 | | - } |
119 | | - |
| 140 | + // Prints a message to the console, even in quiet mode. |
120 | 141 | private void logQuietMessage(String message) { |
121 | 142 | logger.quiet(message) |
122 | 143 | } |
| 144 | +} |
| 145 | + |
| 146 | +class ListExamplesTask extends DefaultTask { |
| 147 | + |
| 148 | + // Regex for extracting descriptions. Isolates the Javadoc comment immediately |
| 149 | + // preceeding the class definition. |
| 150 | + private static final String COMMENT_REGEX = |
| 151 | + '(?s)(/\\*\\*(?:(?!/\\*\\*).)*?\\*/)(?:(?:(?!\\*/).)*?)(?=public class )' |
| 152 | + |
| 153 | + // Regex for comment markers. |
| 154 | + private static final String COMMENT_MARKERS = '(/\\*\\*\\s+|\\*\\s|\\*/)' |
| 155 | + |
| 156 | + // The base directory containing all examples. |
| 157 | + private static final String MAIN_EXAMPLES_BASE_DIRECTORY = |
| 158 | + 'src/main/java/com/google/ads/googleads/examples/' |
| 159 | + |
| 160 | + // The base directory containing all examples. |
| 161 | + private static final String MIGRATION_EXAMPLES_BASE_DIRECTORY = |
| 162 | + '../google-ads-migration-examples/src/main/java/com/google/ads/googleads/migration/' |
| 163 | + |
| 164 | + // The name of the package with the migration examples (currently only one |
| 165 | + // such package exists). |
| 166 | + private static final String MIGRATION_PACKAGE_NAME = 'campaignmanagement' |
| 167 | + |
| 168 | + // A list of subdirectories to exclude from the printout. |
| 169 | + @Input |
| 170 | + List<String> directoriesToExclude = ['utils'] |
| 171 | + |
| 172 | + // An optional subdirectory; if non-null, only examples in the specified |
| 173 | + // subdirectory will be printed. |
| 174 | + @Optional |
| 175 | + @Input |
| 176 | + private String subdirectory = null |
| 177 | + |
| 178 | + // An optional case-sensitive search string; if non-null, only examples whose |
| 179 | + // name or description contains the specified term will be printed. |
| 180 | + @Optional |
| 181 | + @Input |
| 182 | + private String searchTerm = null |
| 183 | + |
| 184 | + ListExamplesTask() { |
| 185 | + description = 'List all available code examples with package name ' + |
| 186 | + 'and description.' |
| 187 | + } |
| 188 | + |
| 189 | + String getSubdirectory() { |
| 190 | + return this.subdirectory |
| 191 | + } |
| 192 | + |
| 193 | + List<String> getDirectoriesToExclude() { |
| 194 | + return this.directoriesToExclude |
| 195 | + } |
| 196 | + |
| 197 | + String getSearchTerm() { |
| 198 | + return this.searchTerm |
| 199 | + } |
| 200 | + |
| 201 | + @Option(option = 'subdirectory', description = 'Sets a subdirectory of ' + |
| 202 | + 'google-ads-examples, for example "basicoperations". Only examples in this ' + |
| 203 | + 'subdirectory will be printed. If not specified, all the examples ' + |
| 204 | + 'will be printed.') |
| 205 | + void setSubdirectory(String subdirectory) { |
| 206 | + this.subdirectory = subdirectory.trim() |
| 207 | + } |
| 208 | + |
| 209 | + @Option(option = 'searchTerm', description = 'Sets a case-sensitive search term, ' + |
| 210 | + 'for example "conversion". If set, only examples whose name or description ' + |
| 211 | + 'contains the specified term will be printed.') |
| 212 | + void setSearchTerm(String searchTerm) { |
| 213 | + this.searchTerm = searchTerm.trim() |
| 214 | + } |
| 215 | + |
| 216 | + // Traverse a given directory, printing the information for each example. |
| 217 | + int traverseDirectory(String dir, int offset) { |
| 218 | + int count = 0 |
| 219 | + |
| 220 | + // Determine the number of leading characters in the directory path. This |
| 221 | + // value is different depending on which subproject we are traversing. |
| 222 | + int baseLength = (subdirectory ? |
| 223 | + project.file(dir).absolutePath.lastIndexOf('/') : |
| 224 | + project.file(dir).absolutePath.length()) + 1 |
| 225 | + |
| 226 | + project.file(dir).traverse( |
| 227 | + type : FileType.FILES, |
| 228 | + preDir : { if (it.name in directoriesToExclude) |
| 229 | + return FileVisitResult.SKIP_SUBTREE }, |
| 230 | + excludeNameFilter : { it in directoriesToExclude }, |
| 231 | + nameFilter : { it.endsWith('.java') }, |
| 232 | + sort : { a, b -> a.name <=> b.name } |
| 233 | + ) { |
| 234 | + // Strip out the base directory, truncate the .java file extension, |
| 235 | + // and replace the / with . to indicate package name. |
| 236 | + String className = it.path.substring(baseLength, |
| 237 | + it.path.length()-5).replaceAll('/','.') |
| 238 | + |
| 239 | + // Fetch the example description. |
| 240 | + Matcher matcher = it.text =~ COMMENT_REGEX |
| 241 | + String description = |
| 242 | + matcher ? matcher[0][0] : "*description missing*" |
| 243 | + |
| 244 | + if (!searchTerm || className.contains(searchTerm) || |
| 245 | + description.contains(searchTerm)) { |
| 246 | + // Print the count, package.class name, and description (without |
| 247 | + // comment markers and trailing newlines). |
| 248 | + println '' + (++count + offset) + ': ' + className + ' - ' + |
| 249 | + description.replaceAll(COMMENT_MARKERS,'').strip() |
| 250 | + } |
| 251 | + } |
| 252 | + |
| 253 | + return count |
| 254 | + } |
123 | 255 |
|
| 256 | + @TaskAction |
| 257 | + void exec() { |
| 258 | + String mainExamplesDirectory = subdirectory ? |
| 259 | + MAIN_EXAMPLES_BASE_DIRECTORY + subdirectory + '/' : MAIN_EXAMPLES_BASE_DIRECTORY |
| 260 | + String migrationExamplesDirectory = subdirectory ? |
| 261 | + MIGRATION_EXAMPLES_BASE_DIRECTORY + subdirectory + '/' : MIGRATION_EXAMPLES_BASE_DIRECTORY |
| 262 | + int count = 0 |
| 263 | + |
| 264 | + count += traverseDirectory(mainExamplesDirectory, count) |
| 265 | + |
| 266 | + // Only print the migration examples if printing all examples or examples |
| 267 | + // with the campaignmanagement package name. |
| 268 | + if (!subdirectory || subdirectory == MIGRATION_PACKAGE_NAME) { |
| 269 | + count += traverseDirectory(migrationExamplesDirectory, count) |
| 270 | + } |
| 271 | + |
| 272 | + println "\nTotal examples: $count" |
| 273 | + } |
124 | 274 | } |
0 commit comments