]> sigrok.org Git - sigrok-androidutils.git/blame - ant/src/org/sigrok/androidutils/ant/CopyLibsTask.java
UsbHelper: Add additional methods for device enumeration and monitoring
[sigrok-androidutils.git] / ant / src / org / sigrok / androidutils / ant / CopyLibsTask.java
CommitLineData
ea3ce762 1/*
5de7ce63 2 * This file is part of the sigrok-androidutils project.
ea3ce762
MC
3 *
4 * Copyright (C) 2014 Marcus Comstedt <marcus@mc.pp.se>
5 *
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.
10 *
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.
15 *
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/>.
18 */
19
20package org.sigrok.androidutils.ant;
21
22import java.io.File;
23import java.io.IOException;
24import java.io.FileInputStream;
25import java.io.FileOutputStream;
26import java.io.InputStream;
27import java.io.OutputStream;
28import java.util.ArrayList;
29import java.util.Arrays;
30import java.util.Collections;
31import java.util.HashMap;
32import java.util.HashSet;
33import java.util.LinkedList;
34import java.util.Queue;
35import java.util.TreeSet;
36import java.util.Vector;
37import org.apache.tools.ant.BuildException;
38import org.apache.tools.ant.Task;
39import org.apache.tools.ant.types.FileSet;
40import org.apache.tools.ant.types.PatternSet;
41import org.apache.tools.ant.types.Resource;
42import org.apache.tools.ant.types.ResourceCollection;
43import org.apache.tools.ant.types.resources.FileProvider;
44import org.apache.tools.ant.types.selectors.SelectorUtils;
45
6f1c3a93
UH
46public class CopyLibsTask extends Task
47{
995e06f3
MC
48 private static final HashMap<String,String> blacklist;
49
50 static {
51 HashMap bl = new HashMap<String,String>();
52 bl.put("libpcre.so", "libercp.so");
53 blacklist = bl;
54 }
55
6f1c3a93
UH
56 private static BuildException buildException(Exception e)
57 {
58 if (e instanceof BuildException)
59 return (BuildException)e;
ea3ce762 60 else
6f1c3a93 61 return new BuildException(e);
ea3ce762
MC
62 }
63
6f1c3a93 64 private static int indexOf(byte[] data, byte v, int start)
ea3ce762 65 {
6f1c3a93
UH
66 if (data != null) {
67 for (int i = start; i < data.length; i++) {
68 if (data[i] == v)
69 return i;
70 }
71 }
72 return -1;
ea3ce762
MC
73 }
74
6f1c3a93 75 private static String fixSoname(String s)
ea3ce762 76 {
6f1c3a93
UH
77 int l = s.length();
78 int i = s.lastIndexOf(".so");
ea3ce762 79
6f1c3a93 80 if (i >= 0 && i < (l - 3))
995e06f3
MC
81 s = s.substring(0, i + 3);
82
83 String bl = blacklist.get(s);
84 if (bl != null)
85 s = bl;
86
87 return s;
ea3ce762
MC
88 }
89
6f1c3a93 90 protected class Library implements Comparable<Library>
ea3ce762 91 {
6f1c3a93
UH
92 protected final File file;
93 protected final ElfFile elf;
94 protected final HashSet<String> needed;
95 protected final Vector<String> rpath;
96 protected final TreeSet<Range> fixups;
97 protected final String subdir;
98 protected String soname, destname;
99 protected final HashSet<Library> dependencies;
100 protected boolean dependedUpon;
101
102 protected class Range implements Comparable<Range>
103 {
104 public final long start, end;
995e06f3 105 public final byte[] replacement;
6f1c3a93
UH
106
107 public int compareTo(Range r)
108 {
109 if (start < r.start)
110 return -1;
111 else if (start > r.start)
112 return 1;
113 else if (end < r.end)
114 return -1;
115 else if (end > r.end)
116 return 1;
117 else
118 return 0;
119 }
120
995e06f3 121 public Range(long start, long end, byte[] replacement)
6f1c3a93
UH
122 {
123 this.start = start;
124 this.end = end;
995e06f3
MC
125 this.replacement = replacement;
126 }
127
128 public Range(long start, long end)
129 {
130 this(start, end, null);
6f1c3a93 131 }
ea3ce762 132 }
ea3ce762 133
6f1c3a93
UH
134 public int compareTo(Library l)
135 {
136 return destname.compareTo(l.destname);
137 }
ea3ce762 138
6f1c3a93
UH
139 protected void addNeeded(String so)
140 {
141 needed.add(so);
142 }
ea3ce762 143
6f1c3a93
UH
144 protected void addRpath(String rp)
145 {
146 for (String p : rp.split(":")) {
147 if (!rpath.contains(p))
148 rpath.add(p);
149 }
150 }
ea3ce762 151
6f1c3a93
UH
152 private String getDynstr(ElfFile.Dynamic d, byte[] s,
153 long base) throws Exception
154 {
155 int offs = (int)d.d_val;
156 int nul = indexOf(s, (byte)0, offs);
157
158 if (nul < 0)
159 throw new Exception("Invalid dynamic string");
160
161 String name = new String(s, offs, nul-offs, "US-ASCII");
162 offs += base;
163
0c38754c
MC
164 if (d.d_tag == ElfFile.DT_RPATH ||
165 d.d_tag == ElfFile.DT_RUNPATH) {
6f1c3a93
UH
166 // Zap rpath,
167 fixups.add(new Range(offs, offs + name.length()));
168 } else {
169 String fix = fixSoname(name);
170 if (fix.length() < name.length()) {
171 fixups.add(new Range(offs + fix.length(),
172 offs + name.length()));
173 }
995e06f3
MC
174 if (!fix.equals(name.substring(0, fix.length()))) {
175 fixups.add(new Range(offs, offs + fix.length(), fix.getBytes("US-ASCII")));
176 }
6f1c3a93
UH
177 }
178 return name;
ea3ce762 179 }
6f1c3a93
UH
180
181 private void checkDynamic(ElfFile.SectionHeader dynsh,
182 ElfFile.SectionHeader strsh) throws Exception
183 {
184 byte strs[] = new byte[(int)strsh.sh_size];
185
186 elf.read(strsh, strs);
187
188 for (ElfFile.Dynamic d : elf.readDynamic(dynsh)) {
189 if (d.d_tag == ElfFile.DT_NULL)
190 break;
191 else if (d.d_tag == ElfFile.DT_SONAME)
192 soname = getDynstr(d, strs, strsh.sh_offset);
193 else if (d.d_tag == ElfFile.DT_NEEDED)
194 addNeeded(getDynstr(d, strs, strsh.sh_offset));
0c38754c
MC
195 else if (d.d_tag == ElfFile.DT_RPATH ||
196 d.d_tag == ElfFile.DT_RUNPATH)
6f1c3a93
UH
197 addRpath(getDynstr(d, strs, strsh.sh_offset));
198 }
ea3ce762 199 }
6f1c3a93
UH
200
201 private void checkElf() throws Exception
202 {
203 for (ElfFile.SectionHeader sh : elf.secHeaders) {
204 if (sh.sh_type == ElfFile.SHT_DYNAMIC)
205 checkDynamic(sh, elf.secHeaders[sh.sh_link]);
ea3ce762 206 }
ea3ce762 207 }
ea3ce762 208
6f1c3a93
UH
209 protected File getDestName(File dest)
210 {
211 File d = new File(dest, subdir);
212 File f = new File(d, destname);
213 return f;
214 }
ea3ce762 215
6f1c3a93
UH
216 protected void writeTo(File dest) throws IOException
217 {
218 FileInputStream is = new FileInputStream(file);
219 FileOutputStream os = new FileOutputStream(dest);
220 byte[] buf = new byte[65536];
221 TreeSet<Range> ranges = new TreeSet<Range>(fixups);
222 long offs = 0;
223
224 outer: for(;;) {
225 long next = offs + buf.length;
226 if (!ranges.isEmpty())
227 next = ranges.first().start;
228 if (next > offs) {
229 long chunk = next - offs;
230 if (chunk > buf.length)
231 chunk = buf.length;
232 int r = is.read(buf, 0, (int)chunk);
233 if (r < 0)
234 break;
235 os.write(buf, 0, r);
236 offs += r;
237 continue;
238 }
239 while (!ranges.isEmpty() && ranges.first().start <= offs) {
240 Range rg = ranges.pollFirst();
241 if (rg.end > offs) {
242 long chunk = rg.end - offs;
243 while (chunk > 0) {
244 int slice = (chunk > buf.length ? buf.length : (int)chunk);
245 int r = is.read(buf, 0, slice);
246 if (r < 0)
247 break outer;
248 if (r > 0) {
995e06f3
MC
249 if (rg.replacement == null)
250 Arrays.fill(buf, 0, r, (byte)0);
251 else
252 System.arraycopy(rg.replacement, (int)(offs-rg.start), buf, 0, r);
6f1c3a93
UH
253 os.write(buf, 0, r);
254 chunk -= r;
255 }
256 }
257 offs = rg.end;
258 }
259 }
260 }
ea3ce762 261
6f1c3a93
UH
262 os.close();
263 is.close();
264 }
ea3ce762 265
6f1c3a93
UH
266 protected Library(File f, String s) throws Exception
267 {
268 file = f;
269 subdir = s;
270 elf = new ElfFile(file);
271 needed = new HashSet<String>();
272 rpath = new Vector<String>();
273 fixups = new TreeSet<Range>();
274 soname = f.getName();
275 dependencies = new HashSet<Library>();
276 dependedUpon = false;
277 checkElf();
278 destname = fixSoname(soname);
279 }
ea3ce762 280
6f1c3a93
UH
281 protected Library(Resource r) throws Exception
282 {
283 this(r.as(FileProvider.class).getFile(),
284 new File(r.getName()).getParent());
285 }
ea3ce762 286
6f1c3a93
UH
287 public String toString()
288 {
289 return "Library(" + file + ")";
290 }
ea3ce762 291
6f1c3a93 292 };
ea3ce762 293
6f1c3a93 294 protected class Worker
ea3ce762 295 {
6f1c3a93
UH
296 protected final int machine;
297 protected final Queue<Library> workQueue;
298 protected final HashMap<String,Library> knownLibs;
299 protected final HashSet<Library> processedLibs;
300 protected final HashSet<String> allDests;
301 protected final Vector<String> rpath;
302
303 protected void addWork(Library l)
304 {
305 if (l == null)
306 return;
307 Library kl = knownLibs.get(l.soname);
308 if (kl == l)
309 return; // Already processed.
310 if (kl != null)
311 throw new BuildException("Multiple libs with the same soname " + l.soname);
312 knownLibs.put(l.soname, l);
313 if (allDests.contains(l.destname))
314 throw new BuildException("Multiple libs with simplified soname " + l.destname);
315 allDests.add(l.destname);
316 workQueue.add(l);
317 }
318
319 protected void addRpath(Vector<String> rp)
320 {
321 for (String p : rp) {
322 if (!rpath.contains(p))
323 rpath.add(p);
324 }
325 }
326
327 protected void setDependency(Library l1, Library l2)
328 {
ed3a4c3c 329 if (l2 == null) // Dependency on external lib.
6f1c3a93
UH
330 return;
331 l1.dependencies.add(l2);
332 l2.dependedUpon = true;
333 }
334
335 protected Library findLibInRpath(String s, String subdir)
336 throws Exception
337 {
338 for (String p : rpath) {
339 File f = new File(p, s);
340 if (f.exists()) {
341 Library l = new Library(f, subdir);
342 if (l.elf.header.e_machine == machine)
343 return l;
344 }
345 }
346 return null;
347 }
348
349 protected Library getLibForSoname(String s, String subdir)
350 throws Exception
351 {
352 Library l = knownLibs.get(s);
353 if (l != null)
354 return l;
355 boolean include = false;
4785171b
MC
356 String[] includePatterns = patterns.getIncludePatterns(getProject());
357 if (includePatterns != null) {
358 for (String patt : includePatterns) {
359 if (SelectorUtils.match(patt, s)) {
360 include = true;
361 break;
362 }
6f1c3a93
UH
363 }
364 }
365 if (!include) {
4785171b
MC
366 String[] excludePatterns = patterns.getExcludePatterns(getProject());
367 if (excludePatterns != null) {
368 for (String patt : excludePatterns) {
369 if (SelectorUtils.match(patt, s))
370 return null;
371 }
6f1c3a93
UH
372 }
373 }
374 l = findLibInRpath(s, subdir);
375 if (l == null)
376 throw new Exception("Library " + s + " not found");
377 addWork(l);
ea3ce762
MC
378 return l;
379 }
6f1c3a93
UH
380
381 protected void process(Library l) throws Exception
382 {
383 if (processedLibs.contains(l))
ed3a4c3c 384 return; // Already processed.
6f1c3a93
UH
385 processedLibs.add(l);
386 addRpath(l.rpath);
387 for (String need : l.needed)
388 setDependency(l, getLibForSoname(need, l.subdir));
389 }
390
391 protected Vector<Library> topoSort(HashSet<Library> libs)
392 {
393 Vector<Library> order = new Vector<Library>();
394 for (Library chk : new HashSet<Library>(libs)) {
395 if (!chk.dependedUpon)
396 libs.remove(chk);
397 }
398 while (!libs.isEmpty()) {
399 HashSet<Library> leafs = new HashSet<Library>();
400 for (Library chk : new HashSet<Library>(libs)) {
401 if (chk.dependencies.isEmpty())
402 leafs.add(chk);
403 }
404 if (leafs.isEmpty())
405 throw new BuildException("Circular dependency found");
406 ArrayList<Library> llist = new ArrayList<Library>(leafs);
407 Collections.sort(llist);
408 order.addAll(llist);
409 libs.removeAll(leafs);
410 for (Library l : libs)
411 l.dependencies.removeAll(leafs);
412 }
413 return order;
414 }
415
416 protected void execute() throws BuildException
417 {
418 try {
419 while (!workQueue.isEmpty())
420 process(workQueue.remove());
421 } catch (Exception e) {
422 throw buildException(e);
423 }
424 if (property != null) {
425 Vector<Library> order =
ed3a4c3c 426 topoSort(new HashSet<Library>(processedLibs));
6f1c3a93
UH
427 StringBuilder sb = new StringBuilder();
428 for (Library l : order) {
429 String name = l.destname;
430 if (name.startsWith("lib"))
431 name = name.substring(3);
432 if (name.endsWith(".so"))
433 name = name.substring(0, name.length() - 3);
434 sb.append(" <item>");
435 sb.append(name);
436 sb.append("</item>\n");
437 }
438 String orderedLibs = sb.toString();
439 getProject().setNewProperty(property, orderedLibs);
440 }
441 for (Library chk : new HashSet<Library>(processedLibs)) {
442 File dest = chk.getDestName(destDir);
443 if (dest.exists() &&
444 dest.lastModified() >= chk.file.lastModified())
445 processedLibs.remove(chk);
446 dest = dest.getParentFile();
447 if (!dest.exists())
448 dest.mkdirs();
449 }
450 if (processedLibs.isEmpty())
451 return;
452 log("Copying " + processedLibs.size() + " libraries into " + destDir);
453 ArrayList<Library> libs = new ArrayList<Library>(processedLibs);
454 Collections.sort(libs);
455 try {
456 for (Library l : libs)
457 l.writeTo(l.getDestName(destDir));
458 } catch (Exception e) {
459 throw buildException(e);
460 }
461 }
462
463 protected Worker(int mach)
464 {
465 machine = mach;
466 workQueue = new LinkedList<Library>();
467 knownLibs = new HashMap<String,Library>();
468 processedLibs = new HashSet<Library>();
469 allDests = new HashSet<String>();
470 rpath = new Vector<String>();
471 }
472
473 };
474
475 protected File destDir = null; // The destination directory.
476 protected Vector<ResourceCollection> rcs = new Vector<ResourceCollection>();
477 protected PatternSet patterns = new PatternSet();
478 protected String property = null;
479
480 public void setTodir(File destDir)
481 {
482 this.destDir = destDir;
ea3ce762
MC
483 }
484
6f1c3a93 485 public void addFileset(FileSet set)
ea3ce762 486 {
6f1c3a93 487 add(set);
ea3ce762
MC
488 }
489
6f1c3a93 490 public void add(ResourceCollection res)
ea3ce762 491 {
6f1c3a93 492 rcs.add(res);
ea3ce762
MC
493 }
494
6f1c3a93 495 public PatternSet.NameEntry createExclude()
ea3ce762 496 {
6f1c3a93 497 return patterns.createExclude();
ea3ce762
MC
498 }
499
6f1c3a93 500 public PatternSet.NameEntry createInclude()
ea3ce762 501 {
6f1c3a93 502 return patterns.createInclude();
ea3ce762
MC
503 }
504
6f1c3a93 505 public void setProperty(String prop)
ea3ce762 506 {
6f1c3a93 507 property = prop;
ea3ce762 508 }
6f1c3a93
UH
509
510 public void execute() throws BuildException
511 {
512 HashMap<Integer,Worker> workers = new HashMap<Integer,Worker>();
513 final int size = rcs.size();
514
515 for (int i = 0; i < size; i++) {
516 ResourceCollection rc = rcs.elementAt(i);
517 for (Resource r : rc) {
518 if (!r.isExists()) {
519 String message = "Could not find library "
520 + r.toLongString() + " to copy.";
521 throw new BuildException(message);
522 }
523 Library l;
524 try {
525 l = new Library(r);
526 } catch (Exception e) {
527 throw buildException(e);
528 }
529 Integer m = new Integer(l.elf.header.e_machine);
530 Worker w = workers.get(m);
531 if (w == null)
532 workers.put(m, (w = new Worker(m.intValue())));
533 w.addWork(l);
534 }
ea3ce762 535 }
6f1c3a93
UH
536 ArrayList<Integer> machines = new ArrayList<Integer>(workers.keySet());
537 Collections.sort(machines);
538 for (Integer m : machines)
539 workers.get(m).execute();
ea3ce762 540 }
ea3ce762 541}