1 // %NO_EDIT_WARNING%
2
3 ////////////////////////////////////////////////////////////////////////
4 //
5 // Copyright (C) 2008-2021 The Octave Project Developers
6 //
7 // See the file COPYRIGHT.md in the top-level directory of this
8 // distribution or <https://octave.org/copyright/>.
9 //
10 // This file is part of Octave.
11 //
12 // Octave is free software: you can redistribute it and/or modify it
13 // under the terms of the GNU General Public License as published by
14 // the Free Software Foundation, either version 3 of the License, or
15 // (at your option) any later version.
16 //
17 // Octave is distributed in the hope that it will be useful, but
18 // WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 // GNU General Public License for more details.
21 //
22 // You should have received a copy of the GNU General Public License
23 // along with Octave; see the file COPYING. If not, see
24 // <https://www.gnu.org/licenses/>.
25 //
26 ////////////////////////////////////////////////////////////////////////
27
28 #if defined (HAVE_CONFIG_H)
29 # include "config.h"
30 #endif
31
32 #include <string>
33 #include <cstring>
34 #include <map>
35 #include <list>
36 #include <algorithm>
37 #include <iostream>
38 #include <fstream>
39 #include <sstream>
40 #include <vector>
41 #include <cstdlib>
42
43 // Programming note: The CROSS macro here refers to building a
44 // cross-compiler aware version of mkoctfile that can be used to cross
45 // compile .oct file for Windows builds of Octave, not that mkoctfile
46 // itself is being cross compiled.
47 //
48 // We don't use the wrapper and gnulib functions when we are building
49 // with CROSS defined. This configuration is only expected to work on
50 // modern systems that should not need to have gnulib to fix POSIX
51 // portability problems. So we just assume a working POSIX system when
52 // CROSS is defined.
53
54 #if defined (CROSS)
55 # include <stdlib.h>
56 # include <sys/types.h>
57 # include <sys/wait.h>
58 # include <unistd.h>
59 # ifndef OCTAVE_UNUSED
60 # define OCTAVE_UNUSED
61 # endif
62 #else
63 # include "mkostemps-wrapper.h"
64 # include "uniconv-wrappers.h"
65 # include "unistd-wrappers.h"
66 # include "wait-wrappers.h"
67 #endif
68
69 static std::map<std::string, std::string> vars;
70
71 #if ! defined (OCTAVE_VERSION)
72 # define OCTAVE_VERSION %OCTAVE_CONF_VERSION%
73 #endif
74
75 #if ! defined (OCTAVE_PREFIX)
76 # define OCTAVE_PREFIX %OCTAVE_CONF_PREFIX%
77 #endif
78
79 #if ! defined (OCTAVE_EXEC_PREFIX)
80 # define OCTAVE_EXEC_PREFIX %OCTAVE_CONF_EXEC_PREFIX%
81 #endif
82
83 #include "shared-fcns.h"
84
85 #if defined (CROSS)
86
87 static int
octave_mkostemps_wrapper(char * tmpl,int suffixlen)88 octave_mkostemps_wrapper (char *tmpl, int suffixlen)
89 {
90 return mkostemps (tmpl, suffixlen, 0);
91 }
92
93 static char *
octave_u8_conv_to_encoding(const char * tocode,const uint8_t * src,std::size_t srclen,std::size_t * lengthp)94 octave_u8_conv_to_encoding (const char *tocode, const uint8_t *src,
95 std::size_t srclen, std::size_t *lengthp)
96 {
97 // FIXME: Do we need to provide the conversion here?
98 return nullptr;
99 }
100
101 static int
octave_unlink_wrapper(const char * nm)102 octave_unlink_wrapper (const char *nm)
103 {
104 return unlink (nm);
105 }
106
107 static bool
octave_wifexited_wrapper(int status)108 octave_wifexited_wrapper (int status)
109 {
110 return WIFEXITED (status);
111 }
112
113 static int
octave_wexitstatus_wrapper(int status)114 octave_wexitstatus_wrapper (int status)
115 {
116 return WEXITSTATUS (status);
117 }
118
119 #endif
120
121 static std::string
get_line(FILE * fp)122 get_line (FILE *fp)
123 {
124 std::ostringstream buf;
125
126 while (true)
127 {
128 int c = std::fgetc (fp);
129
130 if (c == '\n' || c == EOF)
131 break;
132
133 buf << static_cast<char> (c);
134 }
135
136 return buf.str ();
137 }
138
139 static std::string
get_variable(const char * name,const std::string & defval)140 get_variable (const char *name, const std::string& defval)
141 {
142 const char *val = getenv (name);
143
144 if (val && *val)
145 return std::string (val);
146 else
147 return defval;
148 }
149
150 static std::string
quote_path(const std::string & s)151 quote_path (const std::string& s)
152 {
153 if (s.find (' ') != std::string::npos && s[0] != '"')
154 return '"' + s + '"';
155 else
156 return s;
157 }
158
159 static std::string
replace_prefix(std::string s)160 replace_prefix (std::string s)
161 {
162 #if defined (OCTAVE_REPLACE_PREFIX)
163 const std::string match = "${prefix}";
164 std::size_t pos = s.find (match);
165 while (pos != std::string::npos )
166 {
167 s.replace(pos, match.length (), prepend_octave_exec_home (""));
168 pos = s.find (match);
169 }
170 #endif
171
172 return s;
173 }
174
175 static void
initialize(void)176 initialize (void)
177 {
178 set_octave_home ();
179
180 vars["OCTAVE_HOME"] = Voctave_home;
181 vars["OCTAVE_EXEC_HOME"] = Voctave_exec_home;
182
183 vars["API_VERSION"] = %OCTAVE_API_VERSION%;
184 vars["CANONICAL_HOST_TYPE"] = %OCTAVE_CANONICAL_HOST_TYPE%;
185 vars["DEFAULT_PAGER"] = %OCTAVE_DEFAULT_PAGER%;
186 vars["EXEEXT"] = %OCTAVE_EXEEXT%;
187 vars["MAN1EXT"] = %OCTAVE_MAN1EXT%;
188 vars["OCTAVE_VERSION"] = %OCTAVE_VERSION%;
189
190 vars["ARCHLIBDIR"] = prepend_octave_exec_home (%OCTAVE_ARCHLIBDIR%);
191 vars["BINDIR"] = prepend_octave_exec_home (%OCTAVE_BINDIR%);
192 vars["DATADIR"] = prepend_octave_home (%OCTAVE_DATADIR%);
193 vars["DATAROOTDIR"] = prepend_octave_home (%OCTAVE_DATAROOTDIR%);
194 vars["FCNFILEDIR"] = prepend_octave_home (%OCTAVE_FCNFILEDIR%);
195 vars["IMAGEDIR"] = prepend_octave_home (%OCTAVE_IMAGEDIR%);
196 vars["INFODIR"] = prepend_octave_home (%OCTAVE_INFODIR%);
197 vars["INFOFILE"] = prepend_octave_home (%OCTAVE_INFOFILE%);
198 vars["LIBEXECDIR"] = prepend_octave_exec_home (%OCTAVE_LIBEXECDIR%);
199 vars["LOCALAPIARCHLIBDIR"] = prepend_octave_exec_home (%OCTAVE_LOCALAPIARCHLIBDIR%);
200 vars["LOCALAPIFCNFILEDIR"] = prepend_octave_home (%OCTAVE_LOCALAPIFCNFILEDIR%);
201 vars["LOCALAPIOCTFILEDIR"] = prepend_octave_exec_home (%OCTAVE_LOCALAPIOCTFILEDIR%);
202 vars["LOCALARCHLIBDIR"] = prepend_octave_exec_home (%OCTAVE_LOCALARCHLIBDIR%);
203 vars["LOCALFCNFILEDIR"] = prepend_octave_home (%OCTAVE_LOCALFCNFILEDIR%);
204 vars["LOCALOCTFILEDIR"] = prepend_octave_exec_home (%OCTAVE_LOCALOCTFILEDIR%);
205 vars["LOCALSTARTUPFILEDIR"] = prepend_octave_home (%OCTAVE_LOCALSTARTUPFILEDIR%);
206 vars["LOCALVERARCHLIBDIR"] = prepend_octave_exec_home (%OCTAVE_LOCALVERARCHLIBDIR%);
207 vars["LOCALVERFCNFILEDIR"] = prepend_octave_home (%OCTAVE_LOCALVERFCNFILEDIR%);
208 vars["LOCALVEROCTFILEDIR"] = prepend_octave_exec_home (%OCTAVE_LOCALVEROCTFILEDIR%);
209 vars["MAN1DIR"] = prepend_octave_home (%OCTAVE_MAN1DIR%);
210 vars["MANDIR"] = prepend_octave_home (%OCTAVE_MANDIR%);
211 vars["OCTDATADIR"] = prepend_octave_home (%OCTAVE_OCTDATADIR%);
212 vars["OCTDOCDIR"] = prepend_octave_home (%OCTAVE_OCTDOCDIR%);
213 vars["OCTFILEDIR"] = prepend_octave_exec_home (%OCTAVE_OCTFILEDIR%);
214 vars["OCTFONTSDIR"] = prepend_octave_home (%OCTAVE_OCTFONTSDIR%);
215 vars["STARTUPFILEDIR"] = prepend_octave_home (%OCTAVE_STARTUPFILEDIR%);
216
217 vars["OCTINCLUDEDIR"]
218 = get_variable ("OCTINCLUDEDIR",
219 prepend_octave_home (%OCTAVE_CONF_OCTINCLUDEDIR%));
220
221 vars["INCLUDEDIR"]
222 = get_variable ("INCLUDEDIR",
223 prepend_octave_home (%OCTAVE_CONF_INCLUDEDIR%));
224
225 vars["LIBDIR"]
226 = get_variable ("LIBDIR", prepend_octave_exec_home (%OCTAVE_CONF_LIBDIR%));
227
228 vars["OCTLIBDIR"]
229 = get_variable ("OCTLIBDIR",
230 prepend_octave_exec_home (%OCTAVE_CONF_OCTLIBDIR%));
231
232 #if defined (OCTAVE_USE_WINDOWS_API)
233 std::string DEFAULT_INCFLAGS
234 = "-I" + quote_path (vars["OCTINCLUDEDIR"] + R"(\..)")
235 + " -I" + quote_path (vars["OCTINCLUDEDIR"]);
236 #else
237 std::string DEFAULT_INCFLAGS
238 = "-I" + quote_path (vars["OCTINCLUDEDIR"] + "/..")
239 + " -I" + quote_path (vars["OCTINCLUDEDIR"]);
240 #endif
241
242 if (vars["INCLUDEDIR"] != "/usr/include")
243 DEFAULT_INCFLAGS += " -I" + quote_path (vars["INCLUDEDIR"]);
244
245 std::string DEFAULT_LDFLAGS;
246
247 #if (defined (OCTAVE_USE_WINDOWS_API) || defined (CROSS)) || (defined __APPLE__ && defined __MACH__)
248
249 // We'll be linking the files we compile with -loctinterp and -loctave,
250 // so we need to know where to find them.
251 DEFAULT_LDFLAGS += "-L" + quote_path (vars["OCTLIBDIR"]);
252 #endif
253
254 if (vars["LIBDIR"] != "/usr/lib")
255 DEFAULT_LDFLAGS += " -L" + quote_path (vars["LIBDIR"]);
256
257 vars["CPPFLAGS"] = get_variable ("CPPFLAGS",
258 replace_prefix (%OCTAVE_CONF_CPPFLAGS%));
259
260 vars["INCFLAGS"] = get_variable ("INCFLAGS", DEFAULT_INCFLAGS);
261
262 vars["F77"] = get_variable ("F77", %OCTAVE_CONF_MKOCTFILE_F77%);
263
264 vars["FFLAGS"] = get_variable ("FFLAGS", %OCTAVE_CONF_FFLAGS%);
265
266 vars["FPICFLAG"] = get_variable ("FPICFLAG", %OCTAVE_CONF_FPICFLAG%);
267
268 vars["CC"] = get_variable ("CC", %OCTAVE_CONF_MKOCTFILE_CC%);
269
270 vars["CFLAGS"] = get_variable ("CFLAGS", %OCTAVE_CONF_CFLAGS%);
271
272 vars["CPICFLAG"] = get_variable ("CPICFLAG", %OCTAVE_CONF_CPICFLAG%);
273
274 vars["CXX"] = get_variable ("CXX", %OCTAVE_CONF_MKOCTFILE_CXX%);
275
276 vars["CXXFLAGS"] = get_variable ("CXXFLAGS", %OCTAVE_CONF_CXXFLAGS%);
277
278 vars["CXXLD"] = get_variable ("CXXLD", vars["CXX"]);
279
280 vars["CXXPICFLAG"] = get_variable ("CXXPICFLAG", %OCTAVE_CONF_CXXPICFLAG%);
281
282 vars["XTRA_CFLAGS"] = get_variable ("XTRA_CFLAGS", %OCTAVE_CONF_XTRA_CFLAGS%);
283
284 vars["XTRA_CXXFLAGS"] = get_variable ("XTRA_CXXFLAGS",
285 %OCTAVE_CONF_XTRA_CXXFLAGS%);
286
287 vars["AR"] = get_variable ("AR", %OCTAVE_CONF_MKOCTFILE_AR%);
288
289 vars["RANLIB"] = get_variable ("RANLIB", %OCTAVE_CONF_MKOCTFILE_RANLIB%);
290
291 vars["DEPEND_FLAGS"] = get_variable ("DEPEND_FLAGS",
292 %OCTAVE_CONF_DEPEND_FLAGS%);
293
294 vars["DEPEND_EXTRA_SED_PATTERN"]
295 = get_variable ("DEPEND_EXTRA_SED_PATTERN",
296 %OCTAVE_CONF_DEPEND_EXTRA_SED_PATTERN%);
297
298 vars["DL_LDFLAGS"] = get_variable ("DL_LDFLAGS",
299 %OCTAVE_CONF_MKOCTFILE_DL_LDFLAGS%);
300
301 vars["RDYNAMIC_FLAG"] = get_variable ("RDYNAMIC_FLAG",
302 %OCTAVE_CONF_RDYNAMIC_FLAG%);
303
304 vars["LIBOCTAVE"] = "-loctave";
305
306 vars["LIBOCTINTERP"] = "-loctinterp";
307
308 vars["READLINE_LIBS"] = "-lreadline";
309
310 vars["LAPACK_LIBS"] = get_variable ("LAPACK_LIBS", %OCTAVE_CONF_LAPACK_LIBS%);
311
312 vars["BLAS_LIBS"] = get_variable ("BLAS_LIBS", %OCTAVE_CONF_BLAS_LIBS%);
313
314 vars["FFTW3_LDFLAGS"]
315 = get_variable ("FFTW3_LDFLAGS",
316 replace_prefix (%OCTAVE_CONF_FFTW3_LDFLAGS%));
317
318 vars["FFTW3_LIBS"] = get_variable ("FFTW3_LIBS", %OCTAVE_CONF_FFTW3_LIBS%);
319
320 vars["FFTW3F_LDFLAGS"]
321 = get_variable ("FFTW3F_LDFLAGS",
322 replace_prefix (%OCTAVE_CONF_FFTW3F_LDFLAGS%));
323
324 vars["FFTW3F_LIBS"] = get_variable ("FFTW3F_LIBS", %OCTAVE_CONF_FFTW3F_LIBS%);
325
326 vars["LIBS"] = get_variable ("LIBS", %OCTAVE_CONF_LIBS%);
327
328 vars["FLIBS"] = get_variable ("FLIBS",
329 replace_prefix (%OCTAVE_CONF_FLIBS%));
330
331 vars["OCTAVE_LINK_DEPS"] = get_variable ("OCTAVE_LINK_DEPS",
332 %OCTAVE_CONF_MKOCTFILE_OCTAVE_LINK_DEPS%);
333
334 vars["OCTAVE_LINK_OPTS"] = get_variable ("OCTAVE_LINK_OPTS",
335 %OCTAVE_CONF_OCTAVE_LINK_OPTS%);
336
337 vars["OCT_LINK_DEPS"] = get_variable ("OCT_LINK_DEPS",
338 %OCTAVE_CONF_MKOCTFILE_OCT_LINK_DEPS%);
339
340 vars["OCT_LINK_OPTS"]
341 = get_variable ("OCT_LINK_OPTS",
342 replace_prefix (%OCTAVE_CONF_OCT_LINK_OPTS%));
343
344 vars["LDFLAGS"] = get_variable ("LDFLAGS",
345 replace_prefix (%OCTAVE_CONF_LDFLAGS%));
346
347 vars["LD_STATIC_FLAG"] = get_variable ("LD_STATIC_FLAG",
348 %OCTAVE_CONF_LD_STATIC_FLAG%);
349
350 // FIXME: Remove LFLAGS in Octave 8.0
351 vars["LFLAGS"] = get_variable ("LFLAGS", DEFAULT_LDFLAGS);
352 if (vars["LFLAGS"] != DEFAULT_LDFLAGS)
353 std::cerr << "warning: LFLAGS is deprecated and will be removed in a future version of Octave, use LDFLAGS instead" << std::endl;
354
355 vars["F77_INTEGER8_FLAG"] = get_variable ("F77_INTEGER8_FLAG",
356 %OCTAVE_CONF_F77_INTEGER_8_FLAG%);
357 vars["ALL_FFLAGS"] = vars["FFLAGS"] + ' ' + vars["F77_INTEGER8_FLAG"];
358
359 vars["ALL_CFLAGS"]
360 = vars["INCFLAGS"] + ' ' + vars["XTRA_CFLAGS"] + ' ' + vars["CFLAGS"];
361
362 vars["ALL_CXXFLAGS"]
363 = vars["INCFLAGS"] + ' ' + vars["XTRA_CXXFLAGS"] + ' ' + vars["CXXFLAGS"];
364
365 vars["ALL_LDFLAGS"]
366 = vars["LD_STATIC_FLAG"] + ' ' + vars["CPICFLAG"] + ' ' + vars["LDFLAGS"];
367
368 vars["OCTAVE_LIBS"]
369 = (vars["LIBOCTINTERP"] + ' ' + vars["LIBOCTAVE"] + ' '
370 + vars["SPECIAL_MATH_LIB"]);
371
372 vars["FFTW_LIBS"] = vars["FFTW3_LDFLAGS"] + ' ' + vars["FFTW3_LIBS"] + ' '
373 + vars["FFTW3F_LDFLAGS"] + ' ' + vars["FFTW3F_LIBS"];
374 }
375
376 static std::string usage_msg = "usage: mkoctfile [options] file ...";
377
378 static std::string version_msg = "mkoctfile, version " OCTAVE_VERSION;
379
380 static bool debug = false;
381
382 static std::string help_msg =
383 "\n"
384 "Options:\n"
385 "\n"
386 " -h, -?, --help Print this message.\n"
387 "\n"
388 " -IDIR Add -IDIR to compile commands.\n"
389 "\n"
390 " -idirafter DIR Add -idirafter DIR to compile commands.\n"
391 "\n"
392 " -DDEF Add -DDEF to compile commands.\n"
393 "\n"
394 " -lLIB Add library LIB to link command.\n"
395 "\n"
396 " -LDIR Add -LDIR to link command.\n"
397 "\n"
398 " -M, --depend Generate dependency files (.d) for C and C++\n"
399 " source files.\n"
400 #if ! defined (OCTAVE_USE_WINDOWS_API)
401 "\n"
402 " -pthread Add -pthread to link command.\n"
403 #endif
404 "\n"
405 " -RDIR Add -RDIR to link command.\n"
406 "\n"
407 " -Wl,... Pass flags though the linker like -Wl,-rpath=...\n"
408 "\n"
409 " -W... Pass flags though the compiler like -Wa,OPTION.\n"
410 "\n"
411 " -c, --compile Compile, but do not link.\n"
412 "\n"
413 " -o FILE, --output FILE Output filename. Default extension is .oct\n"
414 " (or .mex if --mex is specified) unless linking\n"
415 " a stand-alone executable.\n"
416 "\n"
417 " -g Enable debugging options for compilers.\n"
418 "\n"
419 " -p VAR, --print VAR Print configuration variable VAR. There are\n"
420 " three categories of variables:\n"
421 "\n"
422 " Octave configuration variables that users may\n"
423 " override with environment variables. These are\n"
424 " used in commands that mkoctfile executes.\n"
425 "\n"
426 " ALL_CFLAGS INCLUDEDIR\n"
427 " ALL_CXXFLAGS LAPACK_LIBS\n"
428 " ALL_FFLAGS LDFLAGS\n"
429 " ALL_LDFLAGS LD_STATIC_FLAG\n"
430 " BLAS_LIBS LIBDIR\n"
431 " CC LIBOCTAVE\n"
432 " CFLAGS LIBOCTINTERP\n"
433 " CPICFLAG OCTAVE_LINK_OPTS\n"
434 " CPPFLAGS OCTINCLUDEDIR\n"
435 " CXX OCTAVE_LIBS\n"
436 " CXXFLAGS OCTAVE_LINK_DEPS\n"
437 " CXXLD OCTLIBDIR\n"
438 " CXXPICFLAG OCT_LINK_DEPS\n"
439 " DL_LDFLAGS OCT_LINK_OPTS\n"
440 " F77 RDYNAMIC_FLAG\n"
441 " F77_INTEGER8_FLAG SPECIAL_MATH_LIB\n"
442 " FFLAGS XTRA_CFLAGS\n"
443 " FPICFLAG XTRA_CXXFLAGS\n"
444 " INCFLAGS\n"
445 "\n"
446 " Octave configuration variables as above, but\n"
447 " currently unused by mkoctfile.\n"
448 "\n"
449 " AR\n"
450 " DEPEND_EXTRA_SED_PATTERN\n"
451 " DEPEND_FLAGS\n"
452 " FFTW3F_LDFLAGS\n"
453 " FFTW3F_LIBS\n"
454 " FFTW3_LDFLAGS\n"
455 " FFTW3_LIBS\n"
456 " FFTW_LIBS\n"
457 " FLIBS\n"
458 " LIBS\n"
459 " RANLIB\n"
460 " READLINE_LIBS\n"
461 "\n"
462 " Octave configuration variables that are provided\n"
463 " for informational purposes only. Except for\n"
464 " OCTAVE_HOME and OCTAVE_EXEC_HOME, users may not\n"
465 " override these variables.\n"
466 "\n"
467 " If OCTAVE_HOME or OCTAVE_EXEC_HOME are set in\n"
468 " the environment, then other variables are adjusted\n"
469 " accordingly with OCTAVE_HOME or OCTAVE_EXEC_HOME\n"
470 " substituted for the original value of the directory\n"
471 " specified by the --prefix or --exec-prefix options\n"
472 " that were used when Octave was configured.\n"
473 "\n"
474 " API_VERSION LOCALFCNFILEDIR\n"
475 " ARCHLIBDIR LOCALOCTFILEDIR\n"
476 " BINDIR LOCALSTARTUPFILEDIR\n"
477 " CANONICAL_HOST_TYPE LOCALVERARCHLIBDIR\n"
478 " DATADIR LOCALVERFCNFILEDIR\n"
479 " DATAROOTDIR LOCALVEROCTFILEDIR\n"
480 " DEFAULT_PAGER MAN1DIR\n"
481 " EXEC_PREFIX MAN1EXT\n"
482 " EXEEXT MANDIR\n"
483 " FCNFILEDIR OCTAVE_EXEC_HOME\n"
484 " IMAGEDIR OCTAVE_HOME\n"
485 " INFODIR OCTAVE_VERSION\n"
486 " INFOFILE OCTDATADIR\n"
487 " LIBEXECDIR OCTDOCDIR\n"
488 " LOCALAPIARCHLIBDIR OCTFILEDIR\n"
489 " LOCALAPIFCNFILEDIR OCTFONTSDIR\n"
490 " LOCALAPIOCTFILEDIR STARTUPFILEDIR\n"
491 " LOCALARCHLIBDIR\n"
492 "\n"
493 " --link-stand-alone Link a stand-alone executable file.\n"
494 "\n"
495 " --mex Assume we are creating a MEX file. Set the\n"
496 " default output extension to \".mex\".\n"
497 "\n"
498 " -s, --strip Strip output file.\n"
499 "\n"
500 " -n, --just-print, --dry-run\n"
501 " Print commands, but do not execute them.\n"
502 "\n"
503 " -v, --verbose Echo commands as they are executed.\n"
504 "\n"
505 " --silent Ignored. Intended to suppress output from\n"
506 " compiler steps.\n"
507 "\n"
508 " FILE Compile or link FILE. Recognized file types are:\n"
509 "\n"
510 " .c C source\n"
511 " .cc C++ source\n"
512 " .cp C++ source\n"
513 " .cpp C++ source\n"
514 " .CPP C++ source\n"
515 " .cxx C++ source\n"
516 " .c++ C++ source\n"
517 " .C C++ source\n"
518 " .f Fortran source (fixed form)\n"
519 " .F Fortran source (fixed form)\n"
520 " .f90 Fortran source (free form)\n"
521 " .F90 Fortran source (free form)\n"
522 " .o object file\n"
523 " .a library file\n"
524 #if defined (_MSC_VER)
525 " .lib library file\n"
526 #endif
527 "\n";
528
529 static std::string
basename(const std::string & s,bool strip_path=false)530 basename (const std::string& s, bool strip_path = false)
531 {
532 std::string retval;
533
534 std::size_t pos = s.rfind ('.');
535
536 if (pos == std::string::npos)
537 retval = s;
538 else
539 retval = s.substr (0, pos);
540
541 if (strip_path)
542 {
543 std::size_t p1 = retval.rfind ('/'), p2 = retval.rfind ('\\');
544
545 pos = (p1 != std::string::npos && p2 != std::string::npos
546 ? std::max (p1, p2) : (p2 != std::string::npos ? p2 : p1));
547
548 if (pos != std::string::npos)
549 retval = retval.substr (++pos, std::string::npos);
550 }
551
552 return retval;
553 }
554
555 inline bool
starts_with(const std::string & s,const std::string & prefix)556 starts_with (const std::string& s, const std::string& prefix)
557 {
558 return (s.length () >= prefix.length () && s.find (prefix) == 0);
559 }
560
561 inline bool
ends_with(const std::string & s,const std::string & suffix)562 ends_with (const std::string& s, const std::string& suffix)
563 {
564 return (s.length () >= suffix.length ()
565 && s.rfind (suffix) == s.length () - suffix.length ());
566 }
567
568 static int
run_command(const std::string & cmd,bool printonly=false)569 run_command (const std::string& cmd, bool printonly = false)
570 {
571 if (printonly)
572 {
573 std::cout << cmd << std::endl;
574 return 0;
575 }
576
577 if (debug)
578 std::cout << cmd << std::endl;
579
580 int result = system (cmd.c_str ());
581
582 if (octave_wifexited_wrapper (result))
583 result = octave_wexitstatus_wrapper (result);
584
585 return result;
586 }
587
588 bool
is_true(const std::string & s)589 is_true (const std::string& s)
590 {
591 return (s == "yes" || s == "true");
592 }
593
594 static std::string
get_temp_directory(void)595 get_temp_directory (void)
596 {
597 std::string tempd;
598
599 tempd = octave_getenv ("TMPDIR");
600
601 #if defined (__MINGW32__) || defined (_MSC_VER)
602
603 if (tempd.empty ())
604 tempd = octave_getenv ("TEMP");
605
606 if (tempd.empty ())
607 tempd = octave_getenv ("TMP");
608
609 #if defined (P_tmpdir)
610 if (tempd.empty ())
611 tempd = P_tmpdir;
612 #endif
613
614 // Some versions of MinGW and MSVC either don't define P_tmpdir, or
615 // define it to a single backslash. In such cases just use C:\temp.
616 if (tempd.empty () || tempd == R"(\)")
617 tempd = R"(c:\temp)";
618
619 #else
620
621 #if defined (P_tmpdir)
622 if (tempd.empty ())
623 tempd = P_tmpdir;
624 #else
625 if (tempd.empty ())
626 tempd = "/tmp";
627 #endif
628
629 #endif
630
631 return tempd;
632 }
633
634 static std::string
tmp_objfile_name(void)635 tmp_objfile_name (void)
636 {
637 std::string tmpl = get_temp_directory () + "/oct-XXXXXX.o";
638
639 char *ctmpl = new char [tmpl.length () + 1];
640
641 ctmpl = strcpy (ctmpl, tmpl.c_str ());
642
643 // mkostemps will open the file and return a file descriptor. We
644 // won't worry about closing it because we will need the file until we
645 // are done and then the file will be closed when mkoctfile exits.
646 octave_mkostemps_wrapper (ctmpl, 2);
647
648 std::string retval (ctmpl); // make C++ string from filled-in template
649 delete [] ctmpl;
650
651 return retval;
652 }
653
654 static void
clean_up_tmp_files(const std::list<std::string> & tmp_files)655 clean_up_tmp_files (const std::list<std::string>& tmp_files)
656 {
657 for (const auto& file : tmp_files)
658 octave_unlink_wrapper (file.c_str ());
659 }
660
661 int
main(int argc,char ** argv)662 main (int argc, char **argv)
663 {
664 initialize ();
665
666 if (argc == 1)
667 {
668 std::cout << usage_msg << std::endl;
669 return 1;
670 }
671
672 if (argc == 2 && (! strcmp (argv[1], "-v")
673 || ! strcmp (argv[1], "-version")
674 || ! strcmp (argv[1], "--version")))
675 {
676 std::cout << version_msg << std::endl;
677 return 0;
678 }
679
680 std::list<std::string> cfiles, ccfiles, f77files, tmp_objfiles;
681 std::string output_ext = ".oct";
682 std::string objfiles, libfiles, octfile, outputfile;
683 std::string incflags, defs, ldflags, pass_on_options;
684 bool strip = false;
685 bool no_oct_file_strip_on_this_platform = is_true ("%NO_OCT_FILE_STRIP%");
686 bool compile_only = false;
687 bool link_stand_alone = false;
688 bool depend = false;
689 bool printonly = false;
690 bool output_file_option = false;
691
692 for (int i = 1; i < argc; i++)
693 {
694 std::string arg = argv[i];
695
696 std::string file;
697
698 if (ends_with (arg, ".c"))
699 {
700 file = arg;
701 cfiles.push_back (file);
702 }
703 else if (ends_with (arg, ".cc") || ends_with (arg, ".cp")
704 || ends_with (arg, ".cpp") || ends_with (arg, ".CPP")
705 || ends_with (arg, ".cxx") || ends_with (arg, ".c++")
706 || ends_with (arg, ".C"))
707 {
708 file = arg;
709 ccfiles.push_back (file);
710 }
711 else if (ends_with (arg, ".f") || ends_with (arg, ".F")
712 || ends_with (arg, "f90") || ends_with (arg, ".F90"))
713 {
714 file = arg;
715 f77files.push_back (file);
716 }
717 else if (ends_with (arg, ".o") || ends_with (arg, ".obj"))
718 {
719 file = arg;
720 objfiles += (' ' + quote_path (arg));
721 }
722 else if (ends_with (arg, ".lib") || ends_with (arg, ".a"))
723 {
724 file = arg;
725 libfiles += (' ' + quote_path (arg));
726 }
727 else if (arg == "-d" || arg == "-debug" || arg == "--debug"
728 || arg == "-v" || arg == "-verbose" || arg == "--verbose")
729 {
730 debug = true;
731 if (vars["CC"] == "cc-msvc")
732 vars["CC"] += " -d";
733 if (vars["CXX"] == "cc-msvc")
734 vars["CXX"] += " -d";
735 if (vars["CXXLD"] == "cc-msvc")
736 vars["CXXLD"] += " -d";
737 }
738 else if (arg == "-silent" || arg == "--silent")
739 {
740 // Ignored for now.
741 }
742 else if (arg == "-h" || arg == "-?" || arg == "-help" || arg == "--help")
743 {
744 std::cout << usage_msg << std::endl;
745 std::cout << help_msg << std::endl;
746 return 0;
747 }
748 else if (starts_with (arg, "-I"))
749 {
750 incflags += (' ' + quote_path (arg));
751 }
752 else if (arg == "-idirafter")
753 {
754 if (i < argc-1)
755 {
756 arg = argv[++i];
757 incflags += (" -idirafter " + arg);
758 }
759 else
760 std::cerr << "mkoctfile: include directory name missing"
761 << std::endl;
762 }
763 else if (starts_with (arg, "-D"))
764 {
765 defs += (' ' + arg);
766 }
767 else if (arg == "-largeArrayDims" || arg == "-compatibleArrayDims")
768 {
769 std::cerr << "warning: -largeArrayDims and -compatibleArrayDims are accepted for compatibility, but ignored" << std::endl;
770 }
771 else if (starts_with (arg, "-Wl,") || starts_with (arg, "-l")
772 || starts_with (arg, "-L") || starts_with (arg, "-R"))
773 {
774 ldflags += (' ' + quote_path (arg));
775 }
776 #if ! defined (OCTAVE_USE_WINDOWS_API)
777 else if (arg == "-pthread")
778 {
779 ldflags += (' ' + arg);
780 }
781 #endif
782 else if (arg == "-M" || arg == "-depend" || arg == "--depend")
783 {
784 depend = true;
785 }
786 else if (arg == "-o" || arg == "-output" || arg == "--output")
787 {
788 output_file_option = true;
789
790 if (i < argc-1)
791 {
792 arg = argv[++i];
793 outputfile = arg;
794 }
795 else
796 std::cerr << "mkoctfile: output filename missing" << std::endl;
797 }
798 else if (arg == "-n" || arg == "--dry-run" || arg == "--just-print")
799 {
800 printonly = true;
801 }
802 else if (arg == "-p" || arg == "-print" || arg == "--print")
803 {
804 if (i < argc-1)
805 {
806 arg = argv[++i];
807 // FIXME: Remove LFLAGS checking in Octave 7.0
808 if (arg == "LFLAGS")
809 std::cerr << "warning: LFLAGS is deprecated and will be removed in a future version of Octave, use LDFLAGS instead" << std::endl;
810
811 std::cout << vars[arg] << std::endl;
812 return 0;
813 }
814 else
815 std::cerr << "mkoctfile: --print requires argument" << std::endl;
816 }
817 else if (arg == "-s" || arg == "-strip" || arg == "--strip")
818 {
819 if (no_oct_file_strip_on_this_platform)
820 std::cerr << "mkoctfile: stripping disabled on this platform"
821 << std::endl;
822 else
823 strip = true;
824 }
825 else if (arg == "-c" || arg == "-compile" || arg == "--compile")
826 {
827 compile_only = true;
828 }
829 else if (arg == "-g")
830 {
831 vars["ALL_CFLAGS"] += " -g";
832 vars["ALL_CXXFLAGS"] += " -g";
833 vars["ALL_FFLAGS"] += " -g";
834 }
835 else if (arg == "-link-stand-alone" || arg == "--link-stand-alone")
836 {
837 link_stand_alone = true;
838 }
839 else if (arg == "-mex" || arg == "--mex")
840 {
841 incflags += " -I.";
842 #if defined (_MSC_VER)
843 ldflags += " -Wl,-export:mexFunction";
844 #endif
845 output_ext = ".mex";
846 }
847 else if (starts_with (arg, "-W"))
848 {
849 pass_on_options += (' ' + arg);
850 }
851 else if (starts_with (arg, "-O"))
852 {
853 pass_on_options += (' ' + arg);
854 }
855 else if (starts_with (arg, "-"))
856 {
857 // Pass through any unrecognized options.
858 pass_on_options += (' ' + arg);
859 // Check for an additional argument following the option.
860 // However, don't check the final position which is typically a file
861 if (i < argc-2)
862 {
863 arg = argv[i+1];
864 if (arg[0] != '-')
865 {
866 pass_on_options += (' ' + arg);
867 i++;
868 }
869 }
870 }
871 else
872 {
873 std::cerr << "mkoctfile: unrecognized argument " << arg << std::endl;
874 return 1;
875 }
876
877 if (! file.empty () && octfile.empty ())
878 octfile = file;
879 }
880
881 if (output_ext == ".mex"
882 && vars["ALL_CFLAGS"].find ("-g") != std::string::npos)
883 {
884 defs += " -DMEX_DEBUG";
885 }
886
887 if (compile_only && output_file_option
888 && (cfiles.size () + ccfiles.size () + f77files.size ()) > 1)
889 {
890 std::cerr << "mkoctfile: may not use -c and -o with multiple source files"
891 << std::endl;
892 return 1;
893 }
894
895 std::string output_option;
896
897 if (link_stand_alone)
898 {
899 if (! outputfile.empty ())
900 output_option = "-o " + outputfile;
901 }
902 else
903 {
904 if (! outputfile.empty ())
905 {
906 // FIXME: should probably do a better job of finding the
907 // filename extension instead of just looking at the filename
908 // length.
909
910 octfile = outputfile;
911 std::size_t len = octfile.length ();
912 std::size_t len_ext = output_ext.length ();
913 if (len <= len_ext || octfile.substr (len-len_ext) != output_ext)
914 octfile += output_ext;
915 }
916 else
917 octfile = basename (octfile, true) + output_ext;
918 }
919
920 if (depend)
921 {
922 for (const auto& f : cfiles)
923 {
924 std::string dfile = basename (f, true) + ".d", line;
925
926 octave_unlink_wrapper (dfile.c_str ());
927
928 std::string cmd
929 = (vars["CC"] + ' ' + vars["DEPEND_FLAGS"] + ' '
930 + vars["CPPFLAGS"] + ' ' + vars["ALL_CFLAGS"] + ' '
931 + incflags + ' ' + defs + ' ' + quote_path (f));
932
933 // FIXME: Use wide character API for popen on Windows.
934 FILE *fd = popen (cmd.c_str (), "r");
935
936 #if defined (OCTAVE_USE_WINDOWS_API)
937 // FIXME: liboctinterp isn't linked in to mkoctfile.
938 // So we cannot use octave::sys::ofstream. Instead we fall back
939 // on using the functions available from libwrappers.
940 std::size_t srclen = dfile.length ();
941 const uint8_t *src = reinterpret_cast<const uint8_t *>
942 (dfile.c_str ());
943
944 std::size_t length = 0;
945 wchar_t *wchar = reinterpret_cast<wchar_t *>
946 (octave_u8_conv_to_encoding ("wchar_t", src, srclen,
947 &length));
948
949 std::ofstream fo;
950 if (wchar != nullptr)
951 {
952 fo.open (wchar);
953 free (static_cast<void *> (wchar));
954 }
955 else
956 fo.open (dfile.c_str ());
957 #else
958 std::ofstream fo (dfile.c_str ());
959 #endif
960 std::size_t pos;
961 while (! feof (fd))
962 {
963 line = get_line (fd);
964 if ((pos = line.rfind (".o:")) != std::string::npos)
965 {
966 std::size_t spos = line.rfind ('/', pos);
967 std::string ofile
968 = (spos == std::string::npos
969 ? line.substr (0, pos+2)
970 : line.substr (spos+1, pos-spos+1));
971 fo << "pic/" << ofile << ' ' << ofile << ' '
972 << dfile << line.substr (pos) << std::endl;
973 }
974 else
975 fo << line << std::endl;
976 }
977 pclose (fd);
978 fo.close ();
979 }
980
981 for (const auto& f : ccfiles)
982 {
983 std::string dfile = basename (f, true) + ".d", line;
984
985 octave_unlink_wrapper (dfile.c_str ());
986
987 std::string cmd
988 = (vars["CXX"] + ' ' + vars["DEPEND_FLAGS"] + ' '
989 + vars["CPPFLAGS"] + ' ' + vars["ALL_CXXFLAGS"] + ' '
990 + incflags + ' ' + defs + ' ' + quote_path (f));
991
992 // FIXME: Use wide character API for popen on Windows.
993 FILE *fd = popen (cmd.c_str (), "r");
994
995 #if defined (OCTAVE_USE_WINDOWS_API)
996 // FIXME: liboctinterp isn't linked in to mkoctfile.
997 // So we cannot use octave::sys::ofstream. Instead we fall back
998 // on using the functions available from libwrappers.
999 std::size_t srclen = dfile.length ();
1000 const uint8_t *src = reinterpret_cast<const uint8_t *>
1001 (dfile.c_str ());
1002
1003 std::size_t length = 0;
1004 wchar_t *wchar = reinterpret_cast<wchar_t *>
1005 (octave_u8_conv_to_encoding ("wchar_t", src, srclen,
1006 &length));
1007
1008 std::ofstream fo;
1009 if (wchar != nullptr)
1010 {
1011 fo.open (wchar);
1012 free (static_cast<void *> (wchar));
1013 }
1014 else
1015 fo.open (dfile.c_str ());
1016 #else
1017 std::ofstream fo (dfile.c_str ());
1018 #endif
1019 std::size_t pos;
1020 while (! feof (fd))
1021 {
1022 line = get_line (fd);
1023 if ((pos = line.rfind (".o:")) != std::string::npos)
1024 {
1025 std::size_t spos = line.rfind ('/', pos);
1026 std::string ofile
1027 = (spos == std::string::npos
1028 ? line.substr (0, pos+2)
1029 : line.substr (spos+1, pos-spos+1));
1030 fo << "pic/" << ofile << ' ' << ofile << ' '
1031 << dfile << line.substr (pos+2) << std::endl;
1032 }
1033 else
1034 fo << line << std::endl;
1035 }
1036 pclose (fd);
1037 fo.close ();
1038 }
1039
1040 return 0;
1041 }
1042
1043 for (const auto& f : f77files)
1044 {
1045 if (! vars["F77"].empty ())
1046 {
1047 std::string o;
1048 if (compile_only)
1049 {
1050 if (! outputfile.empty ())
1051 o = outputfile;
1052 else
1053 o = basename (f, true) + ".o";
1054 }
1055 else
1056 {
1057 o = tmp_objfile_name ();
1058
1059 tmp_objfiles.push_back (o);
1060
1061 objfiles += (' ' + o);
1062 }
1063
1064 std::string cmd
1065 = (vars["F77"] + " -c " + vars["FPICFLAG"] + ' '
1066 + vars["ALL_FFLAGS"] + ' ' + incflags + ' ' + defs + ' '
1067 + pass_on_options + ' ' + f + " -o " + o);
1068
1069 int status = run_command (cmd, printonly);
1070
1071 if (status)
1072 return status;
1073 }
1074 else
1075 {
1076 std::cerr << "mkoctfile: no way to compile Fortran file " << f
1077 << std::endl;
1078 return 1;
1079 }
1080 }
1081
1082 for (const auto& f : cfiles)
1083 {
1084 if (! vars["CC"].empty ())
1085 {
1086 std::string o;
1087 if (compile_only)
1088 {
1089 if (! outputfile.empty ())
1090 o = outputfile;
1091 else
1092 o = basename (f, true) + ".o";
1093 }
1094 else
1095 {
1096 o = tmp_objfile_name ();
1097
1098 tmp_objfiles.push_back (o);
1099
1100 objfiles += (' ' + o);
1101 }
1102
1103 std::string cmd
1104 = (vars["CC"] + " -c " + vars["CPPFLAGS"] + ' '
1105 + vars["CPICFLAG"] + ' ' + vars["ALL_CFLAGS"] + ' '
1106 + pass_on_options + ' ' + incflags + ' ' + defs + ' '
1107 + quote_path (f) + " -o " + quote_path (o));
1108
1109 int status = run_command (cmd, printonly);
1110
1111 if (status)
1112 return status;
1113 }
1114 else
1115 {
1116 std::cerr << "mkoctfile: no way to compile C file " << f
1117 << std::endl;
1118 return 1;
1119 }
1120 }
1121
1122 for (const auto& f : ccfiles)
1123 {
1124 if (! vars["CXX"].empty ())
1125 {
1126 std::string o;
1127 if (compile_only)
1128 {
1129 if (! outputfile.empty ())
1130 o = outputfile;
1131 else
1132 o = basename (f, true) + ".o";
1133 }
1134 else
1135 {
1136 o = tmp_objfile_name ();
1137
1138 tmp_objfiles.push_back (o);
1139
1140 objfiles += (' ' + o);
1141 }
1142
1143 std::string cmd
1144 = (vars["CXX"] + " -c " + vars["CPPFLAGS"] + ' '
1145 + vars["CXXPICFLAG"] + ' ' + vars["ALL_CXXFLAGS"] + ' '
1146 + pass_on_options + ' ' + incflags + ' ' + defs + ' '
1147 + quote_path (f) + " -o " + quote_path (o));
1148
1149 int status = run_command (cmd, printonly);
1150
1151 if (status)
1152 return status;
1153 }
1154 else
1155 {
1156 std::cerr << "mkoctfile: no way to compile C++ file " << f
1157 << std::endl;
1158 return 1;
1159 }
1160 }
1161
1162 // If we are only compiling, we are done.
1163
1164 if (compile_only)
1165 return 0;
1166
1167 if (objfiles.empty ())
1168 {
1169 std::cerr << "mkoctfile: no objects to link" << std::endl;
1170 return 1;
1171 }
1172
1173 std::string octave_libs;
1174
1175 if (link_stand_alone)
1176 {
1177 if (! vars["CXXLD"].empty ())
1178 {
1179 octave_libs = "-L" + quote_path (vars["OCTLIBDIR"])
1180 + ' ' + vars["OCTAVE_LIBS"];
1181
1182 std::string cmd
1183 = (vars["CXXLD"] + ' ' + vars["CPPFLAGS"] + ' '
1184 + vars["ALL_CXXFLAGS"] + ' ' + vars["RDYNAMIC_FLAG"] + ' '
1185 + pass_on_options + ' ' + output_option + ' ' + objfiles + ' '
1186 + libfiles + ' ' + vars["ALL_LDFLAGS"] + ' ' + ldflags + ' '
1187 + vars["LFLAGS"] + ' ' + octave_libs + ' '
1188 + vars["OCTAVE_LINK_OPTS"] + ' ' + vars["OCTAVE_LINK_DEPS"]);
1189
1190 int status = run_command (cmd, printonly);
1191
1192 clean_up_tmp_files (tmp_objfiles);
1193
1194 if (status)
1195 return status;
1196 }
1197 else
1198 {
1199 std::cerr
1200 << "mkoctfile: no way to link stand-alone executable file"
1201 << std::endl;
1202 return 1;
1203 }
1204 }
1205 else
1206 {
1207 #if defined (OCTAVE_USE_WINDOWS_API) || defined(CROSS)
1208 octave_libs = "-L" + quote_path (vars["OCTLIBDIR"])
1209 + ' ' + vars["OCTAVE_LIBS"];
1210 #endif
1211
1212 std::string cmd
1213 = (vars["CXXLD"] + ' ' + vars["ALL_CXXFLAGS"] + ' '
1214 + pass_on_options + " -o " + octfile + ' ' + objfiles + ' '
1215 + libfiles + ' ' + vars["DL_LDFLAGS"] + ' ' + vars["LDFLAGS"] + ' '
1216 + ldflags + ' ' + vars["LFLAGS"] + ' ' + octave_libs + ' '
1217 + vars["OCT_LINK_OPTS"] + ' ' + vars["OCT_LINK_DEPS"]);
1218
1219 #if defined (OCTAVE_USE_WINDOWS_API) || defined(CROSS)
1220 if (! f77files.empty () && ! vars["FLIBS"].empty ())
1221 cmd += ' ' + vars["FLIBS"];
1222 #endif
1223
1224 int status = run_command (cmd, printonly);
1225
1226 clean_up_tmp_files (tmp_objfiles);
1227
1228 if (status)
1229 return status;
1230 }
1231
1232 if (strip)
1233 {
1234 std::string cmd = "strip " + octfile;
1235
1236 int status = run_command (cmd, printonly);
1237
1238 if (status)
1239 return status;
1240 }
1241
1242 return 0;
1243 }
1244