Initial commit of vpp code.
[vpp.git] / vppversion / lt.c
1 /*
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
25 typedef enum {
26 #define _(m) MODE_##m,
27   foreach_libtool_mode
28 #undef _
29 } lt_mode_t;
30
31 typedef 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
41 typedef struct {
42   lt_edit_type_t type;
43   u8 * data;
44 } lt_edit_t;
45
46 typedef struct {
47   u8 * path;
48 } lt_lib_path_t;
49
50 typedef 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
62 static lt_lib_path_t *
63 search_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
91 static 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
110 static 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
119 static 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
155 static 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
170 static 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
183 static 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
200 static 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
217 static 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
232 static 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
254 static 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
276 static 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
292 static 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
419 static 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
432 static 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
570 int 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 }