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    triple = DEFAULT_TARGET_TRIPLE_STRING;
106  }
107
108  if (sysroot == NULL) {
109    sysroot = "/";
110  }
111
112  ALOGD("Triple: %s, Android sysroot: %s", triple, sysroot);
113
114  // input is in argv[arg_idx]
115  // TODO: Support multiple input files.
116  input = argv[arg_idx];
117
118  return true;
119}
120
121static bool Build(int input_fd, int output_fd,
122                  const char *triple, const char *sysroot) {
123  ABCCompilerDriver *driver = ABCCompilerDriver::Create(triple);
124
125  if (driver == NULL) {
126    return false;
127  }
128
129  driver->setAndroidSysroot(sysroot);
130
131  bool build_result = driver->build(input_fd, output_fd);;
132
133  delete driver;
134
135  return build_result;
136}
137
138static int ProcessFromFd(const char *input, const char *output,
139                         const char *triple, const char *sysroot) {
140  int output_fd, input_fd;
141
142  if (!GetIntArg(output, output_fd)) {
143    ALOGE("Bad output fd '%s'", output);
144    return EXIT_FAILURE;
145  }
146
147  if (!GetIntArg(input, input_fd)) {
148    ALOGE("Bad input fd '%s'", input);
149    return EXIT_FAILURE;
150  }
151
152  if (!Build(input_fd, output_fd, triple, sysroot)) {
153    return EXIT_FAILURE;
154  }
155
156  return EXIT_SUCCESS;
157}
158
159static int ProcessFromFile(const char *input, const char *output,
160                           const char *triple, const char *sysroot) {
161  // TODO: Support multiple input files.
162  int output_fd = -1, input_fd = -1;
163
164  // Open the output file.
165  output_fd = ::open(output, O_RDWR | O_CREAT | O_TRUNC, 0755);
166
167  if (output_fd < 0) {
168    ALOGE("Failed to open %s for output! (%s)", output, strerror(errno));
169    return EXIT_FAILURE;
170  }
171
172  // Open the input file.
173  input_fd = ::open(input, O_RDONLY);
174
175  if (input_fd < 0) {
176    ALOGE("Failed to open %s for input! (%s)", input, strerror(errno));
177    ::close(output_fd);
178    return EXIT_FAILURE;
179  }
180
181  if (!Build(input_fd, output_fd, triple, sysroot)) {
182    ::close(output_fd);
183    ::close(input_fd);
184    return EXIT_FAILURE;
185  }
186
187  ::close(output_fd);
188  ::close(input_fd);
189
190  return EXIT_SUCCESS;
191}
192
193int main(int argc, char **argv) {
194  Mode mode = kUnknownMode;
195  const char *input, *output, *triple = NULL, *sysroot = NULL;
196
197  set_process_name("abcc");
198
199  setvbuf(stdout, NULL, _IONBF, 0);
200
201  init::Initialize();
202
203  if (ParseArguments(argc, argv, mode, input, output, triple, sysroot)) {
204    switch (mode) {
205      case kFdMode: {
206        return ProcessFromFd(input, output, triple, sysroot);
207      }
208      case kFileMode: {
209        return ProcessFromFile(input, output, triple, sysroot);
210      }
211      default: {
212        // Unknown mode encountered. Fall-through to print usage and return
213        // error.
214        break;
215      }
216    }
217    // fall-through
218  }
219
220  usage();
221
222  return EXIT_FAILURE;
223}
224