View Javadoc

1   /*
2    * #%L
3    * xcode-maven-plugin
4    * %%
5    * Copyright (C) 2012 SAP AG
6    * %%
7    * Licensed under the Apache License, Version 2.0 (the "License");
8    * you may not use this file except in compliance with the License.
9    * You may obtain a copy of the License at
10   * 
11   *      http://www.apache.org/licenses/LICENSE-2.0
12   * 
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   * #L%
19   */
20  package com.sap.prd.mobile.ios.mios;
21  
22  import java.io.File;
23  import java.util.Collections;
24  import java.util.HashSet;
25  import java.util.Set;
26  import java.util.logging.LogManager;
27  
28  import org.apache.maven.plugin.AbstractMojo;
29  import org.apache.maven.plugin.MojoExecutionException;
30  import org.apache.maven.project.MavenProject;
31  
32  /**
33   * Base class for Xcode specific mojos
34   * 
35   */
36  public abstract class AbstractXCodeMojo extends AbstractMojo
37  {
38    //
39    // This variable here ensures that a reference to the logger is kept.
40    // According to the API doc of the java.util.logging framework it is required
41    // to keep a reference. Otherwise it might happen that the logger is garbage
42    // collected. This in turn must not happen since the logger keeps a
43    // reference to the maven logger that could not be restored. This variable
44    // should not be accessed. Instead use the LogManager to get the instance.
45    //
46    private static XCodePluginLogger logger = null;
47    
48    static {
49  
50      if(null == LogManager.getLogManager().getLogger(XCodePluginLogger.getLoggerName())) {
51  
52        logger = new XCodePluginLogger();
53        LogManager.getLogManager().addLogger(logger);
54        logger.finest(String.format("XCode plugin logger has been created: %s", logger.getName()));
55      }
56    }
57    
58    /**
59     * The original Xcode sources located in the <code>src/xcode</code> directory stay untouched
60     * during the whole Maven build. However, as we might have to modify the info.plist or the project
61     * itself we copy the whole Xcode source directory during the build into another "checkout"
62     * directory that by default named <code>checkout</code> and located below the Maven build (
63     * <code>target</code>) directory.
64     * 
65     * @parameter expression="${xcode.checkoutDirectory}";
66     */
67    private File checkoutDirectory;
68  
69    /**
70     * The xcode directory of the copied sources below the checkout directory.
71     * 
72     * @parameter expression="${xcode.compileDirectory}"
73     */
74    private File xcodeCompileDirectory;
75  
76    /**
77     * @parameter expression="${project}"
78     * @readonly
79     * @required
80     */
81    protected MavenProject project;
82  
83    /**
84     * The Xcode configurations that shall be built (e.g. Debug and Release).
85     * 
86     * If no configuration is provided in the plugin's <code>configuration</code> section of the
87     * <code>pom.xml</code> it defaults to the values provided in the
88     * <code>defaultAppConfigurations</code> or <code>defaultLibConfigurations</code> parameters
89     * 
90     * @parameter
91     */
92    private Set<String> configurations;
93  
94    /**
95     * @parameter expression="${project.packaging}"
96     * @readonly
97     * @required
98     */
99    protected String packaging;
100 
101   /**
102    * Explicit lists of sdks (iphoneos,iphonesimulator) the Xcode project shall be built for.
103    * 
104    * If no configuration is provided in the plugin's <code>configuration</code> section of the
105    * <code>pom.xml</code> it defaults to the values provided in the <code>defaultAppSdks</code> or
106    * <code>defaultLibSdks</code> parameters
107    * 
108    * @parameter
109    */
110   private Set<String> sdks;
111 
112   /**
113    * Comma separated list of the default Xcode build configurations that should be built for apps
114    * (in contrast to libraries). These values only apply if no "configurations" are explicitly
115    * provided in the POM.
116    * 
117    * @parameter expression="${xcode.app.defaultConfigurations}" default-value="Release,Debug"
118    * @since 1.2.0
119    * 
120    */
121   private String defaultAppConfigurations;
122 
123   /**
124    * Comma separated list of the default Xcode build configurations that should be built for
125    * libraries (in contrast to apps). These values only apply if no "configurations" are explicitly
126    * provided in the POM.
127    * 
128    * @parameter expression="${xcode.lib.defaultConfigurations}" default-value="Release,Debug"
129    * @since 1.2.0
130    * 
131    */
132   private String defaultLibConfigurations;
133 
134   /**
135    * Comma separated list of the default Xcode SDKs that should be used for apps (in contrast to
136    * libs). These values only apply if no "sdks" are explicitly provided in the POM.
137    * 
138    * @parameter expression="${xcode.app.defaultSdks}" default-value="iphoneos,iphonesimulator"
139    * @since 1.2.0
140    * 
141    */
142   private String defaultAppSdks;
143 
144   /**
145    * Comma separated list of the default Xcode SDKs that should be used for libraries (in contrast
146    * to apps). These values only apply if no "sdks" are explicitly provided in the POM.
147    * 
148    * @parameter expression="${xcode.lib.defaultSdks}" default-value="iphoneos,iphonesimulator"
149    * @since 1.2.0
150    * 
151    */
152   private String defaultLibSdks;
153 
154   protected Set<String> getSDKs()
155   {
156     if (sdks == null || sdks.isEmpty()) {
157 
158       try {
159 
160         PackagingType packagingType = PackagingType.getByMavenType(packaging);
161 
162         if (packagingType == PackagingType.APP) {
163 
164           getLog().info(
165                 "No SDKs in POM set. Using default configurations for applications: " + defaultAppSdks);
166           return commaSeparatedStringToSet(defaultAppSdks);
167         }
168         else if (packagingType == PackagingType.LIB || packagingType == PackagingType.FRAMEWORK) {
169           getLog().info(
170                 "No SDKs in POM set. Using default configurations for libraries: " + defaultLibSdks);
171           return commaSeparatedStringToSet(defaultLibSdks);
172         }
173       }
174       catch (PackagingType.UnknownPackagingTypeException e) {
175         getLog().info("Unknown PackagingType found.", e);
176         return Collections.emptySet();
177       }
178     }
179     getLog().info("SDKs have been explicitly set in POM: " + sdks);
180     return sdks;
181   }
182 
183   protected Set<String> getConfigurations()
184   {
185     if (configurations == null || configurations.isEmpty()) {
186 
187       try {
188         PackagingType packagingType = PackagingType.getByMavenType(packaging);
189 
190         if (packagingType == PackagingType.APP) {
191           getLog().info(
192                 "No configurations in POM set. Using default configurations for applications: "
193                       + defaultAppConfigurations);
194           return commaSeparatedStringToSet(defaultAppConfigurations);
195         }
196         else if (packagingType == PackagingType.LIB || packagingType == PackagingType.FRAMEWORK) {
197           getLog()
198             .info(
199                   "No configurations in POM set. Using default configurations for libraries: "
200                         + defaultLibConfigurations);
201           return commaSeparatedStringToSet(defaultLibConfigurations);
202         }
203       }
204       catch (PackagingType.UnknownPackagingTypeException e) {
205         getLog().info("Unknown PackagingType found.", e);
206         return Collections.emptySet();
207       }
208     }
209     getLog().info("Configurations have been explicitly set in POM: " + configurations);
210     return configurations;
211   }
212 
213   private Set<String> commaSeparatedStringToSet(String commaSeparetedValues)
214   {
215     Set<String> values = new HashSet<String>();
216     String[] valueArray = commaSeparetedValues.split(",");
217     for (String value : valueArray) {
218       value = value.trim();
219       if (!value.isEmpty()) {
220         values.add(value);
221       }
222     }
223     return Collections.unmodifiableSet(values);
224   }
225 
226   protected File getCheckoutDirectory()
227   {
228     return checkoutDirectory;
229   }
230 
231   /**
232    * The xcode directory of the copied sources below the checkout directory.
233    */
234   protected File getXCodeCompileDirectory()
235   {
236     return xcodeCompileDirectory;
237   }
238 
239   /**
240    * 
241    * @return the <code>src/xcode</code> directory
242    */
243   protected File getXCodeSourceDirectory()
244   {
245     return new File(project.getBuild().getSourceDirectory());
246   }
247 
248   protected String getFixedProductName(final String productName)
249   {
250     return productName.trim().replaceAll(" ", "");
251   }
252 
253   protected File getXCodeProjectFile()
254   {
255     return new File(getXCodeCompileDirectory(), project.getArtifactId() + ".xcodeproj/project.pbxproj");
256   }
257 
258   /**
259    * Calls a shell script in order to zip a folder. We have to call a shell script as Java cannot
260    * zip symbolic links.
261    * 
262    * @param rootDir
263    *          the directory where the zip command shall be executed
264    * @param zipSubFolder
265    *          the subfolder to be zipped
266    * @param zipFileName
267    *          the name of the zipFile (will be located in the rootDir)
268    * @param archiveFolder
269    *          an optional folder name if the zipSubFolder folder shall be placed inside the zip into
270    *          a parent folder
271    * @return the zip file
272    * @throws MojoExecutionException
273    */
274   protected File zipSubfolder(File rootDir, String zipSubFolder, String zipFileName, String archiveFolder)
275         throws MojoExecutionException
276   {
277     int resultCode = 0;
278 
279     try {
280 
281       File scriptDirectory = new File(project.getBuild().getDirectory(), "scripts").getCanonicalFile();
282       scriptDirectory.deleteOnExit();
283 
284       if (archiveFolder != null)
285       {
286         resultCode = ScriptRunner.copyAndExecuteScript(System.out, "/com/sap/prd/mobile/ios/mios/zip-subfolder.sh",
287               scriptDirectory, rootDir.getCanonicalPath(), zipSubFolder, zipFileName, archiveFolder);
288       }
289       else
290       {
291         resultCode = ScriptRunner.copyAndExecuteScript(System.out, "/com/sap/prd/mobile/ios/mios/zip-subfolder.sh",
292               scriptDirectory, rootDir.getCanonicalPath(), zipSubFolder, zipFileName);
293       }
294     }
295     catch (Exception ex) {
296       throw new MojoExecutionException("Cannot create zip file " + zipFileName + ". Check log for details.", ex);
297     }
298     if (resultCode != 0) {
299       throw new MojoExecutionException("Cannot create zip file " + zipFileName + ". Check log for details.");
300     }
301     getLog().info("Zip file '" + zipFileName + "' created.");
302     return new File(rootDir, zipFileName);
303   }
304 }