1/*
2 * Copyright 2012, The Android Open Source Project
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
17#include <cerrno>
18#include <cstdio>
19#include <cstdlib>
20#include <cstring>
21
22#include <fcntl.h>
23
24#include "bcc/Config/Config.h"
25#include "bcc/Support/Initialization.h"
26#include "bcc/Support/Log.h"
27#include "bcc/AndroidBitcode/ABCCompilerDriver.h"
28
29#include <cutils/process_name.h>
30
31using namespace bcc;
32
33static void usage() {
34  fprintf(stderr, "usage: abcc [--fd output_fd|--file output_filename]\n"
35#ifndef TARGET_BUILD
36                  "            [--triple triple]\n"
37                  "            [--android-sysroot sysroot]\n"
38#endif
39                  "            input_filename(s) or input_fd(s)...\n");
40  return;
41}
42
43static inline bool GetIntArg(const char *arg, int &result) {
44  char *endptr;
45
46  result = ::strtol(arg, &endptr, 0);
47  if (*endptr != '\0') {
48    return false;
49  } else {
50    return true;
51  }
52}
53
54enum Mode {
55  kUnknownMode,
56  kFdMode,
57  kFileMode
58};
59
60static inline bool ParseArguments(int argc, const char *const *argv, Mode &mode,
61                                  const char *&input, const char *&output,
62                                  const char *&triple, const char *&sysroot) {
63  if (argc < 4) {
64    return false;
65  }
66
67  // Parse the mode in argv[1].
68  if (::strcmp(argv[1], "--fd") == 0) {
69    mode = kFdMode;
70  } else if (::strcmp(argv[1], "--file") == 0) {
71    mode = kFileMode;
72  } else {
73    ALOGE("Unknown mode '%s'!", argv[1]);
74    return false;
75  }
76
77  // output is always in argv[2].
78  output = argv[2];
79
80  // On-device version cannot configure the triple and sysroot.
81  int arg_idx = 3;
82#ifndef TARGET_BUILD
83  if (::strcmp(argv[arg_idx], "--triple") == 0) {
84    if ((arg_idx + 2 /* --triple [triple] input */) >= argc) {
85      ALOGE("Too few arguments when --triple was given!");
86      return false;
87    }
88
89    triple = argv[arg_idx + 1];
90    arg_idx += 2;
91  }
92
93  if (::strcmp(argv[arg_idx], "--android-sysroot") == 0) {
94    if ((arg_idx + 2 /* --android-sysroot [sysroot] input */) >= argc) {
95      ALOGE("Too few arguments when --android-sysroot was given!");
96      return false;
97    }
98
99    sysroot = argv[arg_idx + 1];
100    arg_idx += 2;
101  }
102#endif
103
104  if (triple == NULL) {
105#ifdef DEFAULT_ARM_CODEGEN
106    // Generate Thumb instead of ARM.
107    triple = DEFAULT_THUMB_TRIPLE_STRING;
108#else
109     triple = DEFAULT_TARGET_TRIPLE_STRING;
110#endif
111  }
112
113  if (sysroot == NULL) {
114    sysroot = "/";
115  }
116
117  ALOGD("Triple: %s, Android sysroot: %s", triple, sysroot);
118
119  // input is in argv[arg_idx]
120  // TODO: Support multiple input files.
121  input = argv[arg_idx];
122
123  return true;
124}
125
126static bool Build(int input_fd, int output_fd,
127                  const char *triple, const char *sysroot) {
128  ABCCompilerDriver *driver = ABCCompilerDriver::Create(triple);
129
130  if (driver == NULL) {
131    return false;
132  }
133
134  driver->setAndroidSysroot(sysroot);
135
136  bool build_result = driver->build(input_fd, output_fd);;
137
138  delete driver;
139
140  return build_result;
141}
142
143static int ProcessFromFd(const char *input, const char *output,
144                         const char *triple, const char *sysroot) {
145  int output_fd, input_fd;
146
147  if (!GetIntArg(output, output_fd)) {
148    ALOGE("Bad output fd '%s'", output);
149    return EXIT_FAILURE;
150  }
151
152  if (!GetIntArg(input, input_fd)) {
153    ALOGE("Bad input fd '%s'", input);
154    return EXIT_FAILURE;
155  }
156
157  if (!Build(input_fd, output_fd, triple, sysroot)) {
158    return EXIT_FAILURE;
159  }
160
161  return EXIT_SUCCESS;
162}
163
164static int ProcessFromFile(const char *input, const char *output,
165                           const char *triple, const char *sysroot) {
166  // TODO: Support multiple input files.
167  int output_fd = -1, input_fd = -1;
168
169  // Open the output file.
170  output_fd = ::open(output, O_RDWR | O_CREAT | O_TRUNC, 0755);
171
172  if (output_fd < 0) {
173    ALOGE("Failed to open %s for output! (%s)", output, strerror(errno));
174    return EXIT_FAILURE;
175  }
176
177  // Open the input file.
178  input_fd = ::open(input, O_RDONLY);
179
180  if (input_fd < 0) {
181    ALOGE("Failed to open %s for input! (%s)", input, strerror(errno));
182    ::close(output_fd);
183    return EXIT_FAILURE;
184  }
185
186  if (!Build(input_fd, output_fd, triple, sysroot)) {
187    ::close(output_fd);
188    ::close(input_fd);
189    return EXIT_FAILURE;
190  }
191
192  ::close(output_fd);
193  ::close(input_fd);
194
195  return EXIT_SUCCESS;
196}
197
198int main(int argc, char **argv) {
199  Mode mode = kUnknownMode;
200  const char *input, *output, *triple = NULL, *sysroot = NULL;
201
202  set_process_name("abcc");
203
204  setvbuf(stdout, NULL, _IONBF, 0);
205
206  init::Initialize();
207
208  if (ParseArguments(argc, argv, mode, input, output, triple, sysroot)) {
209    switch (mode) {
210      case kFdMode: {
211        return ProcessFromFd(input, output, triple, sysroot);
212      }
213      case kFileMode: {
214        return ProcessFromFile(input, output, triple, sysroot);
215      }
216      default: {
217        // Unknown mode encountered. Fall-through to print usage and return
218        // error.
219        break;
220      }
221    }
222    // fall-through
223  }
224
225  usage();
226
227  return EXIT_FAILURE;
228}
229