View Javadoc
1   /*
2    * Copyright (C) 2007-2012 Argeo GmbH
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *         http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.argeo.slc.lib.linux.rpmfactory;
17  
18  import java.io.File;
19  import java.io.IOException;
20  import java.io.InputStream;
21  import java.io.OutputStream;
22  import java.util.ArrayList;
23  import java.util.List;
24  
25  import org.apache.commons.exec.Executor;
26  import org.apache.commons.io.FileUtils;
27  import org.apache.commons.io.IOUtils;
28  import org.apache.commons.logging.Log;
29  import org.apache.commons.logging.LogFactory;
30  import org.argeo.slc.SlcException;
31  import org.argeo.slc.core.execution.tasks.SystemCall;
32  import org.springframework.core.io.Resource;
33  import org.springframework.core.io.UrlResource;
34  
35  /** Generates an SRPM from a spec file */
36  public class CreateSrpm implements Runnable {
37  	private final static Log log = LogFactory.getLog(CreateSrpm.class);
38  
39  	private File topdir;
40  
41  	/** Directory where to cache downloaded distributions. */
42  	private File distributionCache;
43  
44  	private Resource specFile;
45  
46  	private RpmBuildEnvironment rpmBuildEnvironment;
47  
48  	private Boolean overwriteSources = false;
49  
50  	private File srpmFile;
51  
52  	private Executor executor;
53  
54  	public void run() {
55  		File sourcesDir = new File(topdir, "SOURCES");
56  		sourcesDir.mkdirs();
57  		File specsDir = new File(topdir, "SPECS");
58  		File srpmsDir = new File(topdir, "SRPMS");
59  
60  		try {
61  			// Parse spec file and copy required resources
62  			RpmSpecFile spec = new RpmSpecFile(specFile);
63  			copyToSources(spec, sourcesDir);
64  
65  			// Copy spec file
66  			File targetFile = new File(specsDir, specFile.getFilename())
67  					.getCanonicalFile();
68  			copyResourceToFile(specFile, targetFile);
69  
70  			// Generate rpmbuild config files
71  			rpmBuildEnvironment.writeRpmbuildConfigFiles(topdir);
72  
73  			// Build SRPM
74  			srpmsDir.mkdirs();
75  			SystemCall packageSrpm = new SystemCall();
76  			packageSrpm.arg("rpmbuild");
77  			packageSrpm.arg("-bs").arg("--nodeps");
78  			packageSrpm.arg("--rcfile=rpmrc");
79  			packageSrpm.arg("--macros=" + RpmBuildEnvironment.defaultMacroFiles
80  					+ ":rpmmacros");
81  			// buildSrpm.arg("-D", "_topdir " + topdir.getCanonicalPath() + "");
82  			packageSrpm.arg("SPECS/" + specFile.getFilename());
83  			packageSrpm.setExecDir(topdir.getCanonicalPath());
84  			packageSrpm.setLogCommand(true);
85  
86  			// Execute
87  			packageSrpm.setExecutor(executor);
88  			String answer = packageSrpm.function();
89  
90  			// Extract generated SRPM path
91  			// TODO: make it safer
92  			String srpmPath = answer.split(":")[1].trim();
93  			srpmFile = new File(srpmPath);
94  		} catch (IOException e) {
95  			throw new SlcException("Cannot generate SRPM from " + specFile, e);
96  		}
97  
98  	}
99  
100 	protected void copyToSources(RpmSpecFile spec, File sourcesDir) {
101 		try {
102 			List<Resource> toCopyToSources = new ArrayList<Resource>();
103 			List<Resource> toDownload = new ArrayList<Resource>();
104 			for (String file : spec.getSources().values()) {
105 				try {
106 					Resource res;
107 					try {
108 						res = specFile.createRelative("../SOURCES/" + file);
109 						if (!res.exists())
110 							res = new UrlResource(file);
111 
112 					} catch (Exception e) {
113 						res = new UrlResource(file);
114 						toDownload.add(res);
115 					}
116 					toCopyToSources.add(res);
117 				} catch (Exception e) {
118 					log.error("Cannot interpret " + file, e);
119 				}
120 			}
121 			for (String file : spec.getPatches().values()) {
122 				try {
123 					Resource res;
124 					try {
125 						res = specFile.createRelative("../SOURCES/" + file);
126 						if (!res.exists()) {
127 							res = new UrlResource(file);
128 						}
129 					} catch (Exception e) {
130 						res = new UrlResource(file);
131 						toDownload.add(res);
132 					}
133 					toCopyToSources.add(res);
134 				} catch (Exception e) {
135 					log.error("Cannot interpret " + file, e);
136 				}
137 			}
138 
139 			// FIXME: we may have missed some files here
140 			for (Resource res : toCopyToSources) {
141 				File targetDir;
142 				if (distributionCache != null && toDownload.contains(res)) {
143 					if (!distributionCache.exists())
144 						distributionCache.mkdirs();
145 					targetDir = distributionCache;
146 					if (log.isDebugEnabled())
147 						log.debug("Cache " + res + " in " + targetDir);
148 				} else
149 					targetDir = sourcesDir;
150 				File targetFile = new File(targetDir, res.getFilename())
151 						.getCanonicalFile();
152 				if (!targetFile.exists() || overwriteSources)
153 					copyResourceToFile(res, targetFile);
154 				if (!targetDir.equals(sourcesDir)) {
155 					File fileInSourcesDir = new File(sourcesDir,
156 							targetFile.getName());
157 					if (!fileInSourcesDir.exists()
158 							|| !(fileInSourcesDir.length() == targetFile
159 									.length()))
160 						FileUtils.copyFile(targetFile, fileInSourcesDir);
161 				}
162 			}
163 		} catch (Exception e) {
164 			throw new SlcException("Cannot copy to " + sourcesDir, e);
165 		}
166 	}
167 
168 	private static void copyResourceToFile(Resource res, File targetFile) {
169 		try {
170 			if (targetFile.equals(res.getFile())) {
171 				if (log.isDebugEnabled())
172 					log.debug("Target identical to source, skipping... "
173 							+ targetFile + " <=> " + res);
174 				return;
175 			}
176 		} catch (IOException e1) {
177 			// silent
178 		}
179 
180 		OutputStream out = null;
181 		InputStream in = null;
182 		try {
183 			out = FileUtils.openOutputStream(targetFile);
184 			in = res.getInputStream();
185 			IOUtils.copy(in, out);
186 			if (log.isDebugEnabled())
187 				log.debug("Copied " + targetFile + " from " + res);
188 		} catch (Exception e) {
189 			throw new SlcException("Cannot copy " + res + " to " + targetFile,
190 					e);
191 		} finally {
192 			IOUtils.closeQuietly(in);
193 			IOUtils.closeQuietly(out);
194 		}
195 
196 	}
197 
198 	public void setSpecFile(Resource specFile) {
199 		this.specFile = specFile;
200 	}
201 
202 	public void setTopdir(File topdir) {
203 		this.topdir = topdir;
204 	}
205 
206 	public void setOverwriteSources(Boolean overwriteSources) {
207 		this.overwriteSources = overwriteSources;
208 	}
209 
210 	public File getSrpmFile() {
211 		return srpmFile;
212 	}
213 
214 	public void setRpmBuildEnvironment(RpmBuildEnvironment rpmBuildEnvironment) {
215 		this.rpmBuildEnvironment = rpmBuildEnvironment;
216 	}
217 
218 	public void setDistributionCache(File distributionCache) {
219 		this.distributionCache = distributionCache;
220 	}
221 
222 	public void setExecutor(Executor executor) {
223 		this.executor = executor;
224 	}
225 
226 }