blob: 0de2b06114ef36c38a0aadcc9cc73bd3575c8ba0 [file] [log] [blame]
Ed Warnickecb9cada2015-12-08 15:45:58 -07001/*
2 * Copyright (c) 2015 Cisco and/or its affiliates.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15#include <string.h>
16#include <sys/types.h>
17#include <sys/stat.h>
18#include <sys/wait.h>
19#include <vppinfra/bitmap.h>
20#include <vppinfra/format.h>
21#include <vppinfra/hash.h>
22
23#define foreach_libtool_mode _ (compile) _ (link) _ (install)
24
25typedef enum {
26#define _(m) MODE_##m,
27 foreach_libtool_mode
28#undef _
29} lt_mode_t;
30
31typedef enum {
32 LITERAL,
33 OUTPUT_EXE,
34 OUTPUT_LO,
35 OUTPUT_LA,
36 LT_LIB,
37 NON_LT_LIB,
38 IGNORE,
39} lt_edit_type_t;
40
41typedef struct {
42 lt_edit_type_t type;
43 u8 * data;
44} lt_edit_t;
45
46typedef struct {
47 u8 * path;
48} lt_lib_path_t;
49
50typedef struct {
51 lt_mode_t mode;
52 int link_static;
53 int silent;
54 lt_edit_type_t output_edit_type;
55 u8 * output_file;
56 lt_edit_t * edits;
57 lt_lib_path_t * lib_path;
58 uword * rpath_hash;
59 u8 * tag;
60} lt_main_t;
61
62static lt_lib_path_t *
63search_lib_path (lt_main_t * lm, char * fmt, ...)
64{
65 va_list va;
66 static u8 * file_name, * path_name;
67 lt_lib_path_t * p = 0;
68
69 if (file_name)
70 _vec_len (file_name) = 0;
71
72 va_start (va, fmt);
73 file_name = va_format (file_name, fmt, &va);
74 va_end (va);
75
76 path_name = 0;
77 vec_foreach (p, lm->lib_path)
78 {
79 struct stat st;
80
81 if (path_name)
82 _vec_len (path_name) = 0;
83
84 path_name = format (path_name, "%s/%v%c", p->path, file_name, 0);
85 if (stat ((char *) path_name, &st) >= 0)
86 return p;
87 }
88 return 0;
89}
90
91static u8 * format_libtool_mode (u8 * s, va_list * args)
92{
93 int m = va_arg (*args, int);
94 char * t;
95 switch (m)
96 {
97#define _(f) case MODE_##f: t = #f; break;
98 foreach_libtool_mode;
99#undef _
100 default:
101 t = 0;
102 }
103 if (t)
104 vec_add (s, t, strlen (t));
105 else
106 s = format (s, "unknown 0x%x", m);
107 return s;
108}
109
110static uword unformat_libtool_mode (unformat_input_t * input, va_list * args)
111{
112 int * result = va_arg (*args, int *);
113#define _(m) if (unformat (input, #m)) { *result = MODE_##m; return 1; }
114 foreach_libtool_mode;
115#undef _
116 return 0;
117}
118
119static uword unformat_basename (unformat_input_t * input, va_list * args)
120{
121 u8 ** result = va_arg (*args, u8 **);
122 u8 * suffix = va_arg (*args, u8 *);
123 u8 * current_suffix = suffix;
124 uword c;
125
126 while ((c = unformat_get_input (input)) != UNFORMAT_END_OF_INPUT)
127 {
128 switch (c)
129 {
130 case 0:
131 case ' ':
132 case '\t':
133 case '\n':
134 case '\r':
135 goto fail;
136 }
137
138 vec_add1 (*result, c);
139 if (c == *current_suffix)
140 current_suffix++;
141 else
142 current_suffix = suffix;
143
144 if (*current_suffix == 0)
145 {
146 _vec_len (*result) -= current_suffix - suffix;
147 return 1;
148 }
149 }
150 fail:
151 vec_free (*result);
152 return 0;
153}
154
155static void edit (lt_main_t * lm, lt_edit_type_t type, char * fmt, ...)
156{
157 va_list va;
158 lt_edit_t * e;
159 u8 * s;
160
161 va_start (va, fmt);
162 s = va_format (0, fmt, &va);
163 va_end (va);
164
165 vec_add2 (lm->edits, e, 1);
166 e->type = type;
167 e->data = s;
168}
169
170static u8 * format_argv (u8 * s, va_list * args)
171{
172 u8 ** a = va_arg (*args, u8 **);
173 uword i;
174 for (i = 0; i < vec_len (a) - 1; i++)
175 {
176 if (i > 0)
177 vec_add1 (s, ' ');
178 vec_add (s, a[i], vec_len (a[i]) - 1);
179 }
180 return s;
181}
182
183static u8 * format_dirname (u8 * s, va_list * args)
184{
185 u8 * f = va_arg (*args, u8 *);
186 u8 * t;
187
188 for (t = vec_end (f) - 1; t >= f; t--)
189 {
190 if (t[0] == '/')
191 break;
192 }
193 if (t[0] == '/')
194 vec_add (s, f, t - f);
195 else
196 vec_add1 (s, '.');
197 return s;
198}
199
200static u8 * format_basename (u8 * s, va_list * args)
201{
202 u8 * f = va_arg (*args, u8 *);
203 u8 * t;
204
205 for (t = vec_end (f) - 1; t >= f; t--)
206 {
207 if (t[0] == '/')
208 break;
209 }
210 if (t[0] == '/')
211 vec_add (s, t + 1, vec_end (f) - (t + 1));
212 else
213 vec_add (s, f, vec_len (f));
214 return s;
215}
216
217static void my_system (char * fmt, ...)
218{
219 va_list va;
220 u8 * s;
221
222 va_start (va, fmt);
223 s = va_format (0, fmt, &va);
224 va_end (va);
225
226 vec_add1 (s, 0); /* null terminate */
227 if (system ((char *) s) != 0)
228 clib_error ("%s", s);
229 vec_free (s);
230}
231
232static u8 * my_cmd (char * fmt, ...)
233{
234 va_list va;
235 u8 * s;
236 FILE * result;
237 int c;
238
239 va_start (va, fmt);
240 s = va_format (0, fmt, &va);
241 va_end (va);
242
243 vec_add1 (s, 0); /* null terminate */
244 result = popen ((char *) s, "r");
245 if (! result)
246 clib_error ("%s", s);
247 _vec_len (s) = 0;
248 while ((c = fgetc (result)) != EOF)
249 vec_add1 (s, c);
250 pclose (result);
251 return s;
252}
253
254static void make_file_with_contents (lt_main_t * lm, u8 * contents, char * fmt, ...)
255{
256 va_list va;
257 u8 * s;
258 FILE * f;
259
260 va_start (va, fmt);
261 s = va_format (0, fmt, &va);
262 va_end (va);
263
264 vec_add1 (s, 0); /* null terminate */
265 f = fopen ((char *) s, "w");
266
267 if (! f)
268 clib_error ("fopen %s", s);
269
270 if (1 != fwrite (contents, vec_len (contents), 1, f))
271 clib_error ("fwrite");
272
273 fclose (f);
274}
275
276static u8 ** add_argv (u8 ** argv, char * fmt, ...)
277{
278 va_list va;
279 u8 * s;
280
281 va_start (va, fmt);
282 s = va_format (0, fmt, &va);
283 va_end (va);
284 vec_add1 (s, 0); /* null terminate */
285 vec_add1 (argv, s);
286 return argv;
287}
288
289#define GEN_ARGV_PIC (1 << 0)
290#define GEN_ARGV_PUNT (1 << 1)
291
292static u8 ** gen_argv (lt_main_t * lm, uword flags)
293{
294 u8 ** r = 0;
295 uword * path_used_bitmap = 0;
296 lt_edit_t * e;
297 int is_punt;
298
299 is_punt = (flags & GEN_ARGV_PUNT) != 0;
300 if (is_punt)
301 {
302 /* No supported so punt back to shell based libtool. */
303 r = add_argv (r, "/bin/sh");
304 r = add_argv (r, "./libtool");
305 r = add_argv (r, "--mode=%U", format_libtool_mode, lm->mode);
306 }
307
308 if (lm->mode == MODE_compile)
309 ASSERT (lm->output_edit_type != OUTPUT_LA);
310
311 vec_foreach (e, lm->edits)
312 {
313 switch (e->type)
314 {
315 case LITERAL:
316 r = add_argv (r, "%v", e->data);
317 break;
318
319 case OUTPUT_EXE:
320 if (! is_punt)
321 my_system ("mkdir -p %U/.libs", format_dirname, e->data);
322 r = add_argv (r, "-o");
323 r = add_argv (r, "%s%v", is_punt ? "" : ".libs/", e->data);
324 break;
325
326 case OUTPUT_LO:
327 if (flags & GEN_ARGV_PIC)
328 {
329 r = add_argv (r, "-fPIC");
330 r = add_argv (r, "-DPIC");
331 }
332 r = add_argv (r, "-o");
333
334 if (is_punt)
335 r = add_argv (r, "-o %v.lo", e->data);
336
337 else if (flags & GEN_ARGV_PIC)
338 {
339 my_system ("mkdir -p %U/.libs", format_dirname, e->data);
340 r = add_argv (r, "%U/.libs/%U.o",
341 format_dirname, e->data,
342 format_basename, e->data);
343 }
344 else
345 {
346 my_system ("mkdir -p %U", format_dirname, e->data);
347 r = add_argv (r, "%v.o", e->data);
348 }
349 break;
350
351 case OUTPUT_LA:
352 if (is_punt)
353 r = add_argv (r, "-o %v.la", e->data);
354 else
355 abort ();
356 break;
357
358 case LT_LIB:
359 if (is_punt)
360 r = add_argv (r, "%v.la", e->data);
361
362 else if (lm->mode == MODE_link)
363 {
364 u8 * pwd = get_current_dir_name ();
365 u8 * libdir = my_cmd (". %s/%v.la && echo -n ${libdir}", pwd, e->data);
366
367 if (! hash_get_mem (lm->rpath_hash, libdir))
368 {
369 r = add_argv (r, "-Wl,-rpath");
370 r = add_argv (r, "-Wl,%v", libdir);
371 hash_set_mem (lm->rpath_hash, libdir, 0);
372 }
373
374 r = add_argv (r, "%U/.libs/%U.so",
375 format_dirname, e->data,
376 format_basename, e->data);
377 }
378 else
379 r = add_argv (r, "%v.la", e->data);
380 break;
381
382 case NON_LT_LIB:
383 if (lm->mode == MODE_link && ! is_punt)
384 {
385 lt_lib_path_t * p = search_lib_path (lm, "lib%v.so", e->data);
386 if (p)
387 {
388 path_used_bitmap = clib_bitmap_ori (path_used_bitmap, p - lm->lib_path);
389 r = add_argv (r, "%s/lib%v.so", p->path, e->data);
390 }
391 else
392 r = add_argv (r, "-l%v", e->data);
393 }
394
395 else
396 r = add_argv (r, "-l%v", e->data);
397 break;
398
399 default:
400 ASSERT (0);
401 }
402 }
403
404 {
405 uword i;
406 clib_bitmap_foreach (i, path_used_bitmap, ({
407 lt_lib_path_t * p = vec_elt_at_index (lm->lib_path, i);
408 r = add_argv (r, "-Wl,-rpath");
409 r = add_argv (r, "-Wl,%s", p->path);
410 }));
411 clib_bitmap_free (path_used_bitmap);
412 }
413
414 vec_add1 (r, 0);
415
416 return r;
417}
418
419static void do_command (lt_main_t * lm, u8 ** argv)
420{
421 u8 * cmd = format (0, "%U%c", format_argv, argv, 0);
422
423 if (! lm->silent)
424 fformat (stderr, "lt: %s\n", cmd);
425
426 if (system ((char *) cmd))
427 exit (1);
428
429 vec_free (cmd);
430}
431
432static int lt_main (unformat_input_t * input)
433{
434 lt_main_t _lm = {0}, * lm = &_lm;
435 clib_error_t * error = 0;
436 u8 * s;
437
438 lm->rpath_hash = hash_create_vec (0, sizeof (u8), sizeof (uword));
439 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
440 {
441 s = 0;
442
443 if (s)
444 _vec_len (s) = 0;
445
446 if (unformat (input, "-o %s", &s))
447 {
448 u8 * dot = vec_end (s) - 4;
449 int is_la = 0, is_lo = 0;
450
451 is_lo = vec_len (s) >= 4 && ! strcmp ((char *) dot, ".lo");
452 is_la = vec_len (s) >= 4 && ! strcmp ((char *) dot, ".la");
453 if (is_lo || is_la)
454 {
455 dot[0] = 0;
456 lm->output_edit_type = is_lo ? OUTPUT_LO : OUTPUT_LA;
457 }
458 else
459 lm->output_edit_type = OUTPUT_EXE;
460 edit (lm, lm->output_edit_type, "%s", s);
461 lm->output_file = format (0, "%s", s);
462 }
463
464 else if (unformat (input, "-L%s", &s))
465 {
466 lt_lib_path_t * p;
467 vec_add2 (lm->lib_path, p, 1);
468 p->path = s;
469 edit (lm, LITERAL, "-L%s", s);
470 }
471
472 else if (unformat (input, "%U", unformat_basename, &s, ".la"))
473 edit (lm, LT_LIB, "%v", s);
474
475 else if (unformat (input, "-l%s", &s))
476 edit (lm, NON_LT_LIB, "%s", s);
477
478 else if (unformat (input, "--mode=%U", unformat_libtool_mode, &lm->mode))
479 ;
480
481 else if (unformat (input, "--tag=%s", &lm->tag))
482 ;
483
484 else if (unformat (input, "-static"))
485 {
486 lm->link_static = 1;
487 edit (lm, LITERAL, "%s", "-static");
488 }
489
490 else if (unformat (input, "%s", &s))
491 edit (lm, LITERAL, "%s", s);
492
493 else
494 {
495 error = clib_error_create ("parse error `%U'",
496 format_unformat_error, input);
497 goto done;
498 }
499 }
500
501 {
502 u8 ** argv;
503
504 if (! (lm->mode == MODE_compile
505 || (lm->mode == MODE_link && lm->output_edit_type == OUTPUT_EXE && ! lm->link_static)))
506 {
507 argv = gen_argv (lm, GEN_ARGV_PUNT);
508 do_command (lm, argv);
509 }
510 else if (lm->mode == MODE_compile)
511 {
512 argv = gen_argv (lm, GEN_ARGV_PIC);
513 do_command (lm, argv);
514 argv = gen_argv (lm, 0);
515 do_command (lm, argv);
516 }
517 else
518 {
519 argv = gen_argv (lm, 0);
520 do_command (lm, argv);
521 }
522
523 if (lm->mode == MODE_compile)
524 {
525 u8 * s = 0;
526 u8 * f = lm->output_file;
527
528 /* Need this or .lo files are rejected. */
529 s = format (s, "# Generated by libtool (Eliot lt 0.0)\n");
530
531 s = format (s, "pic_object='.libs/%U.o'\n", format_basename, f);
532 s = format (s, "non_pic_object='%U.o'\n", format_basename, f);
533 make_file_with_contents (lm, s, "%v.lo", f);
534 vec_free (s);
535 }
536 else if (lm->mode == MODE_link)
537 {
538 u8 * s = 0;
539 u8 * f = lm->output_file;
540 s = format (s, "%s",
541 "# Generated by libtool (Eliot lt) 2.4\n"
542 "# %%%MAGIC variable%%%\n"
543 "generated_by_libtool_version=2.4\n");
544 make_file_with_contents (lm, s, "%v", f);
545 vec_free (s);
546 }
547
548 {
549 int status;
550 while (1)
551 {
552 if (waitpid (-1, &status, 0) < 0 && errno == ECHILD)
553 break;
554 }
555 exit (0);
556 }
557 }
558
559 done:
560 if (s)
561 vec_free (s);
562 if (error)
563 {
564 clib_error_report (error);
565 return 1;
566 }
567 return 0;
568}
569
570int main (int argc, char * argv[])
571{
572 unformat_input_t i;
573
574 unformat_init_command_line (&i, argv);
575 exit (lt_main (&i));
576 return 0;
577}