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