2 * This file is part of the sigrok-androidutils project.
4 * Copyright (C) 2014 Marcus Comstedt <marcus@mc.pp.se>
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 package org.sigrok.androidutils.ant;
23 import java.io.IOException;
24 import java.io.FileInputStream;
25 import java.io.FileOutputStream;
26 import java.io.InputStream;
27 import java.io.OutputStream;
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.Collections;
31 import java.util.HashMap;
32 import java.util.HashSet;
33 import java.util.LinkedList;
34 import java.util.Queue;
35 import java.util.TreeSet;
36 import java.util.Vector;
37 import org.apache.tools.ant.BuildException;
38 import org.apache.tools.ant.Task;
39 import org.apache.tools.ant.types.FileSet;
40 import org.apache.tools.ant.types.PatternSet;
41 import org.apache.tools.ant.types.Resource;
42 import org.apache.tools.ant.types.ResourceCollection;
43 import org.apache.tools.ant.types.resources.FileProvider;
44 import org.apache.tools.ant.types.selectors.SelectorUtils;
46 public class CopyLibsTask extends Task
48 private static BuildException buildException(Exception e)
50 if (e instanceof BuildException)
51 return (BuildException)e;
53 return new BuildException(e);
56 private static int indexOf(byte[] data, byte v, int start)
59 for (int i = start; i < data.length; i++) {
67 private static String fixSoname(String s)
70 int i = s.lastIndexOf(".so");
72 if (i >= 0 && i < (l - 3))
73 return s.substring(0, i + 3);
78 protected class Library implements Comparable<Library>
80 protected final File file;
81 protected final ElfFile elf;
82 protected final HashSet<String> needed;
83 protected final Vector<String> rpath;
84 protected final TreeSet<Range> fixups;
85 protected final String subdir;
86 protected String soname, destname;
87 protected final HashSet<Library> dependencies;
88 protected boolean dependedUpon;
90 protected class Range implements Comparable<Range>
92 public final long start, end;
94 public int compareTo(Range r)
98 else if (start > r.start)
100 else if (end < r.end)
102 else if (end > r.end)
108 public Range(long start, long end)
115 public int compareTo(Library l)
117 return destname.compareTo(l.destname);
120 protected void addNeeded(String so)
125 protected void addRpath(String rp)
127 for (String p : rp.split(":")) {
128 if (!rpath.contains(p))
133 private String getDynstr(ElfFile.Dynamic d, byte[] s,
134 long base) throws Exception
136 int offs = (int)d.d_val;
137 int nul = indexOf(s, (byte)0, offs);
140 throw new Exception("Invalid dynamic string");
142 String name = new String(s, offs, nul-offs, "US-ASCII");
145 if (d.d_tag == ElfFile.DT_RPATH ||
146 d.d_tag == ElfFile.DT_RUNPATH) {
148 fixups.add(new Range(offs, offs + name.length()));
150 String fix = fixSoname(name);
151 if (fix.length() < name.length()) {
152 fixups.add(new Range(offs + fix.length(),
153 offs + name.length()));
159 private void checkDynamic(ElfFile.SectionHeader dynsh,
160 ElfFile.SectionHeader strsh) throws Exception
162 byte strs[] = new byte[(int)strsh.sh_size];
164 elf.read(strsh, strs);
166 for (ElfFile.Dynamic d : elf.readDynamic(dynsh)) {
167 if (d.d_tag == ElfFile.DT_NULL)
169 else if (d.d_tag == ElfFile.DT_SONAME)
170 soname = getDynstr(d, strs, strsh.sh_offset);
171 else if (d.d_tag == ElfFile.DT_NEEDED)
172 addNeeded(getDynstr(d, strs, strsh.sh_offset));
173 else if (d.d_tag == ElfFile.DT_RPATH ||
174 d.d_tag == ElfFile.DT_RUNPATH)
175 addRpath(getDynstr(d, strs, strsh.sh_offset));
179 private void checkElf() throws Exception
181 for (ElfFile.SectionHeader sh : elf.secHeaders) {
182 if (sh.sh_type == ElfFile.SHT_DYNAMIC)
183 checkDynamic(sh, elf.secHeaders[sh.sh_link]);
187 protected File getDestName(File dest)
189 File d = new File(dest, subdir);
190 File f = new File(d, destname);
194 protected void writeTo(File dest) throws IOException
196 FileInputStream is = new FileInputStream(file);
197 FileOutputStream os = new FileOutputStream(dest);
198 byte[] buf = new byte[65536];
199 TreeSet<Range> ranges = new TreeSet<Range>(fixups);
203 long next = offs + buf.length;
204 if (!ranges.isEmpty())
205 next = ranges.first().start;
207 long chunk = next - offs;
208 if (chunk > buf.length)
210 int r = is.read(buf, 0, (int)chunk);
217 while (!ranges.isEmpty() && ranges.first().start <= offs) {
218 Range rg = ranges.pollFirst();
220 long chunk = rg.end - offs;
222 int slice = (chunk > buf.length ? buf.length : (int)chunk);
223 int r = is.read(buf, 0, slice);
227 Arrays.fill(buf, 0, r, (byte)0);
241 protected Library(File f, String s) throws Exception
245 elf = new ElfFile(file);
246 needed = new HashSet<String>();
247 rpath = new Vector<String>();
248 fixups = new TreeSet<Range>();
249 soname = f.getName();
250 dependencies = new HashSet<Library>();
251 dependedUpon = false;
253 destname = fixSoname(soname);
256 protected Library(Resource r) throws Exception
258 this(r.as(FileProvider.class).getFile(),
259 new File(r.getName()).getParent());
262 public String toString()
264 return "Library(" + file + ")";
269 protected class Worker
271 protected final int machine;
272 protected final Queue<Library> workQueue;
273 protected final HashMap<String,Library> knownLibs;
274 protected final HashSet<Library> processedLibs;
275 protected final HashSet<String> allDests;
276 protected final Vector<String> rpath;
278 protected void addWork(Library l)
282 Library kl = knownLibs.get(l.soname);
284 return; // Already processed.
286 throw new BuildException("Multiple libs with the same soname " + l.soname);
287 knownLibs.put(l.soname, l);
288 if (allDests.contains(l.destname))
289 throw new BuildException("Multiple libs with simplified soname " + l.destname);
290 allDests.add(l.destname);
294 protected void addRpath(Vector<String> rp)
296 for (String p : rp) {
297 if (!rpath.contains(p))
302 protected void setDependency(Library l1, Library l2)
304 if (l2 == null) // Dependency on external lib.
306 l1.dependencies.add(l2);
307 l2.dependedUpon = true;
310 protected Library findLibInRpath(String s, String subdir)
313 for (String p : rpath) {
314 File f = new File(p, s);
316 Library l = new Library(f, subdir);
317 if (l.elf.header.e_machine == machine)
324 protected Library getLibForSoname(String s, String subdir)
327 Library l = knownLibs.get(s);
330 boolean include = false;
331 for (String patt : patterns.getIncludePatterns(getProject())) {
332 if (SelectorUtils.match(patt, s)) {
338 for (String patt : patterns.getExcludePatterns(getProject())) {
339 if (SelectorUtils.match(patt, s))
343 l = findLibInRpath(s, subdir);
345 throw new Exception("Library " + s + " not found");
350 protected void process(Library l) throws Exception
352 if (processedLibs.contains(l))
353 return; // Already processed.
354 processedLibs.add(l);
356 for (String need : l.needed)
357 setDependency(l, getLibForSoname(need, l.subdir));
360 protected Vector<Library> topoSort(HashSet<Library> libs)
362 Vector<Library> order = new Vector<Library>();
363 for (Library chk : new HashSet<Library>(libs)) {
364 if (!chk.dependedUpon)
367 while (!libs.isEmpty()) {
368 HashSet<Library> leafs = new HashSet<Library>();
369 for (Library chk : new HashSet<Library>(libs)) {
370 if (chk.dependencies.isEmpty())
374 throw new BuildException("Circular dependency found");
375 ArrayList<Library> llist = new ArrayList<Library>(leafs);
376 Collections.sort(llist);
378 libs.removeAll(leafs);
379 for (Library l : libs)
380 l.dependencies.removeAll(leafs);
385 protected void execute() throws BuildException
388 while (!workQueue.isEmpty())
389 process(workQueue.remove());
390 } catch (Exception e) {
391 throw buildException(e);
393 if (property != null) {
394 Vector<Library> order =
395 topoSort(new HashSet<Library>(processedLibs));
396 StringBuilder sb = new StringBuilder();
397 for (Library l : order) {
398 String name = l.destname;
399 if (name.startsWith("lib"))
400 name = name.substring(3);
401 if (name.endsWith(".so"))
402 name = name.substring(0, name.length() - 3);
403 sb.append(" <item>");
405 sb.append("</item>\n");
407 String orderedLibs = sb.toString();
408 getProject().setNewProperty(property, orderedLibs);
410 for (Library chk : new HashSet<Library>(processedLibs)) {
411 File dest = chk.getDestName(destDir);
413 dest.lastModified() >= chk.file.lastModified())
414 processedLibs.remove(chk);
415 dest = dest.getParentFile();
419 if (processedLibs.isEmpty())
421 log("Copying " + processedLibs.size() + " libraries into " + destDir);
422 ArrayList<Library> libs = new ArrayList<Library>(processedLibs);
423 Collections.sort(libs);
425 for (Library l : libs)
426 l.writeTo(l.getDestName(destDir));
427 } catch (Exception e) {
428 throw buildException(e);
432 protected Worker(int mach)
435 workQueue = new LinkedList<Library>();
436 knownLibs = new HashMap<String,Library>();
437 processedLibs = new HashSet<Library>();
438 allDests = new HashSet<String>();
439 rpath = new Vector<String>();
444 protected File destDir = null; // The destination directory.
445 protected Vector<ResourceCollection> rcs = new Vector<ResourceCollection>();
446 protected PatternSet patterns = new PatternSet();
447 protected String property = null;
449 public void setTodir(File destDir)
451 this.destDir = destDir;
454 public void addFileset(FileSet set)
459 public void add(ResourceCollection res)
464 public PatternSet.NameEntry createExclude()
466 return patterns.createExclude();
469 public PatternSet.NameEntry createInclude()
471 return patterns.createInclude();
474 public void setProperty(String prop)
479 public void execute() throws BuildException
481 HashMap<Integer,Worker> workers = new HashMap<Integer,Worker>();
482 final int size = rcs.size();
484 for (int i = 0; i < size; i++) {
485 ResourceCollection rc = rcs.elementAt(i);
486 for (Resource r : rc) {
488 String message = "Could not find library "
489 + r.toLongString() + " to copy.";
490 throw new BuildException(message);
495 } catch (Exception e) {
496 throw buildException(e);
498 Integer m = new Integer(l.elf.header.e_machine);
499 Worker w = workers.get(m);
501 workers.put(m, (w = new Worker(m.intValue())));
505 ArrayList<Integer> machines = new ArrayList<Integer>(workers.keySet());
506 Collections.sort(machines);
507 for (Integer m : machines)
508 workers.get(m).execute();