view src/mkoctfile.in.cc @ 28240:2fb684dc2ec2

axis.m: Implement "fill" option for Matlab compatibility. * axis.m: Document that "fill" is a synonym for "normal". Place "vis3d" option in documentation table for modes which affect aspect ratio. Add strcmpi (opt, "fill") to decode opt and executed the same behavior as "normal".
author Rik <rik@octave.org>
date Fri, 24 Apr 2020 13:16:09 -0700
parents 7818c5b07403
children 159b6a1eb408
line wrap: on
line source

// %NO_EDIT_WARNING%

////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2008-2020 The Octave Project Developers
//
// See the file COPYRIGHT.md in the top-level directory of this
// distribution or <https://octave.org/copyright/>.
//
// This file is part of Octave.
//
// Octave is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Octave is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Octave; see the file COPYING.  If not, see
// <https://www.gnu.org/licenses/>.
//
////////////////////////////////////////////////////////////////////////

#if defined (HAVE_CONFIG_H)
#  include "config.h"
#endif

#include <string>
#include <cstring>
#include <map>
#include <list>
#include <algorithm>
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <cstdlib>

// Programming note:  The CROSS macro here refers to building a
// cross-compiler aware version of mkoctfile that can be used to cross
// compile .oct file for Windows builds of Octave, not that mkoctfile
// itself is being cross compiled.
//
// We don't use the wrapper and gnulib functions when we are building
// with CROSS defined.  This configuration is only expected to work on
// modern systems that should not need to have gnulib to fix POSIX
// portability problems.  So we just assume a working POSIX system when
// CROSS is defined.

#if defined (CROSS)
#  include <stdlib.h>
#  include <sys/types.h>
#  include <sys/wait.h>
#  include <unistd.h>
#  ifndef OCTAVE_UNUSED
#    define OCTAVE_UNUSED
#  endif
#else
#  include "mkostemps-wrapper.h"
#  include "unistd-wrappers.h"
#  include "wait-wrappers.h"
#endif

static std::map<std::string, std::string> vars;

#if ! defined (OCTAVE_VERSION)
#  define OCTAVE_VERSION %OCTAVE_CONF_VERSION%
#endif

#if ! defined (OCTAVE_PREFIX)
#  define OCTAVE_PREFIX %OCTAVE_CONF_PREFIX%
#endif

#if ! defined (OCTAVE_EXEC_PREFIX)
#  define OCTAVE_EXEC_PREFIX %OCTAVE_CONF_EXEC_PREFIX%
#endif

#include "shared-fcns.h"

#if defined (CROSS)

static int
octave_mkostemps_wrapper (char *tmpl, int suffixlen)
{
  return mkostemps (tmpl, suffixlen, 0);
}

static int
octave_unlink_wrapper (const char *nm)
{
  return unlink (nm);
}

static bool
octave_wifexited_wrapper (int status)
{
  return WIFEXITED (status);
}

static int
octave_wexitstatus_wrapper (int status)
{
  return WEXITSTATUS (status);
}

#endif

static std::string
get_line (FILE *fp)
{
  std::ostringstream buf;

  while (true)
    {
      int c = std::fgetc (fp);

      if (c == '\n' || c == EOF)
        break;

      buf << static_cast<char> (c);
    }

  return buf.str ();
}

static std::string
get_variable (const char *name, const std::string& defval)
{
  const char *val = getenv (name);

  if (val && *val)
    return std::string (val);
  else
    return defval;
}

static std::string
quote_path (const std::string& s)
{
  if (s.find (' ') != std::string::npos && s[0] != '"')
    return '"' + s + '"';
  else
    return s;
}

static void
initialize (void)
{
  set_octave_home ();

  vars["OCTAVE_HOME"] = Voctave_home;
  vars["OCTAVE_EXEC_HOME"] = Voctave_exec_home;

  vars["API_VERSION"] = %OCTAVE_API_VERSION%;
  vars["CANONICAL_HOST_TYPE"] = %OCTAVE_CANONICAL_HOST_TYPE%;
  vars["DEFAULT_PAGER"] = %OCTAVE_DEFAULT_PAGER%;
  vars["EXEEXT"] = %OCTAVE_EXEEXT%;
  vars["MAN1EXT"] = %OCTAVE_MAN1EXT%;
  vars["OCTAVE_VERSION"] = %OCTAVE_VERSION%;

  vars["ARCHLIBDIR"] = prepend_octave_exec_home (%OCTAVE_ARCHLIBDIR%);
  vars["BINDIR"] = prepend_octave_exec_home (%OCTAVE_BINDIR%);
  vars["DATADIR"] = prepend_octave_home (%OCTAVE_DATADIR%);
  vars["DATAROOTDIR"] = prepend_octave_home (%OCTAVE_DATAROOTDIR%);
  vars["FCNFILEDIR"] = prepend_octave_home (%OCTAVE_FCNFILEDIR%);
  vars["IMAGEDIR"] = prepend_octave_home (%OCTAVE_IMAGEDIR%);
  vars["INFODIR"] = prepend_octave_home (%OCTAVE_INFODIR%);
  vars["INFOFILE"] = prepend_octave_home (%OCTAVE_INFOFILE%);
  vars["LIBEXECDIR"] = prepend_octave_exec_home (%OCTAVE_LIBEXECDIR%);
  vars["LOCALAPIARCHLIBDIR"] = prepend_octave_exec_home (%OCTAVE_LOCALAPIARCHLIBDIR%);
  vars["LOCALAPIFCNFILEDIR"] = prepend_octave_home (%OCTAVE_LOCALAPIFCNFILEDIR%);
  vars["LOCALAPIOCTFILEDIR"] = prepend_octave_exec_home (%OCTAVE_LOCALAPIOCTFILEDIR%);
  vars["LOCALARCHLIBDIR"] = prepend_octave_exec_home (%OCTAVE_LOCALARCHLIBDIR%);
  vars["LOCALFCNFILEDIR"] = prepend_octave_home (%OCTAVE_LOCALFCNFILEDIR%);
  vars["LOCALOCTFILEDIR"] = prepend_octave_exec_home (%OCTAVE_LOCALOCTFILEDIR%);
  vars["LOCALSTARTUPFILEDIR"] = prepend_octave_home (%OCTAVE_LOCALSTARTUPFILEDIR%);
  vars["LOCALVERARCHLIBDIR"] = prepend_octave_exec_home (%OCTAVE_LOCALVERARCHLIBDIR%);
  vars["LOCALVERFCNFILEDIR"] = prepend_octave_home (%OCTAVE_LOCALVERFCNFILEDIR%);
  vars["LOCALVEROCTFILEDIR"] = prepend_octave_exec_home (%OCTAVE_LOCALVEROCTFILEDIR%);
  vars["MAN1DIR"] = prepend_octave_home (%OCTAVE_MAN1DIR%);
  vars["MANDIR"] = prepend_octave_home (%OCTAVE_MANDIR%);
  vars["OCTDATADIR"] = prepend_octave_home (%OCTAVE_OCTDATADIR%);
  vars["OCTDOCDIR"] = prepend_octave_home (%OCTAVE_OCTDOCDIR%);
  vars["OCTFILEDIR"] = prepend_octave_exec_home (%OCTAVE_OCTFILEDIR%);
  vars["OCTFONTSDIR"] = prepend_octave_home (%OCTAVE_OCTFONTSDIR%);
  vars["STARTUPFILEDIR"] = prepend_octave_home (%OCTAVE_STARTUPFILEDIR%);

  vars["OCTINCLUDEDIR"]
    = get_variable ("OCTINCLUDEDIR",
                    prepend_octave_home (%OCTAVE_CONF_OCTINCLUDEDIR%));

  vars["INCLUDEDIR"]
    = get_variable ("INCLUDEDIR",
                    prepend_octave_home (%OCTAVE_CONF_INCLUDEDIR%));

  vars["LIBDIR"]
    = get_variable ("LIBDIR", prepend_octave_exec_home (%OCTAVE_CONF_LIBDIR%));

  vars["OCTLIBDIR"]
    = get_variable ("OCTLIBDIR",
                    prepend_octave_exec_home (%OCTAVE_CONF_OCTLIBDIR%));

#if defined (OCTAVE_USE_WINDOWS_API)
  std::string DEFAULT_INCFLAGS
    = "-I" + quote_path (vars["OCTINCLUDEDIR"] + R"(\..)")
      + " -I" + quote_path (vars["OCTINCLUDEDIR"]);
#else
  std::string DEFAULT_INCFLAGS
    = "-I" + quote_path (vars["OCTINCLUDEDIR"] + "/..")
      + " -I" + quote_path (vars["OCTINCLUDEDIR"]);
#endif

  if (vars["INCLUDEDIR"] != "/usr/include")
    DEFAULT_INCFLAGS += " -I" + quote_path (vars["INCLUDEDIR"]);

  std::string DEFAULT_LDFLAGS;

#if (defined (OCTAVE_USE_WINDOWS_API) || defined (CROSS)) || (defined __APPLE__ && defined __MACH__)

  // We'll be linking the files we compile with -loctinterp and -loctave,
  // so we need to know where to find them.
  DEFAULT_LDFLAGS += "-L" + quote_path (vars["OCTLIBDIR"]);
#endif

  if (vars["LIBDIR"] != "/usr/lib")
    DEFAULT_LDFLAGS += " -L" + quote_path (vars["LIBDIR"]);

  vars["CPPFLAGS"] = get_variable ("CPPFLAGS", %OCTAVE_CONF_CPPFLAGS%);

  vars["INCFLAGS"] = get_variable ("INCFLAGS", DEFAULT_INCFLAGS);

  vars["F77"] = get_variable ("F77", %OCTAVE_CONF_MKOCTFILE_F77%);

  vars["FFLAGS"] = get_variable ("FFLAGS", %OCTAVE_CONF_FFLAGS%);

  vars["FPICFLAG"] = get_variable ("FPICFLAG", %OCTAVE_CONF_FPICFLAG%);

  vars["CC"] = get_variable ("CC", %OCTAVE_CONF_MKOCTFILE_CC%);

  vars["CFLAGS"] = get_variable ("CFLAGS", %OCTAVE_CONF_CFLAGS%);

  vars["CPICFLAG"] = get_variable ("CPICFLAG", %OCTAVE_CONF_CPICFLAG%);

  vars["CXX"] = get_variable ("CXX", %OCTAVE_CONF_MKOCTFILE_CXX%);

  vars["CXXFLAGS"] = get_variable ("CXXFLAGS", %OCTAVE_CONF_CXXFLAGS%);

  vars["CXXLD"] = get_variable ("CXXLD", vars["CXX"]);

  vars["CXXPICFLAG"] = get_variable ("CXXPICFLAG", %OCTAVE_CONF_CXXPICFLAG%);

  vars["XTRA_CFLAGS"] = get_variable ("XTRA_CFLAGS", %OCTAVE_CONF_XTRA_CFLAGS%);

  vars["XTRA_CXXFLAGS"] = get_variable ("XTRA_CXXFLAGS",
                                        %OCTAVE_CONF_XTRA_CXXFLAGS%);

  vars["AR"] = get_variable ("AR", %OCTAVE_CONF_MKOCTFILE_AR%);

  vars["RANLIB"] = get_variable ("RANLIB", %OCTAVE_CONF_MKOCTFILE_RANLIB%);

  vars["DEPEND_FLAGS"] = get_variable ("DEPEND_FLAGS",
                                       %OCTAVE_CONF_DEPEND_FLAGS%);

  vars["DEPEND_EXTRA_SED_PATTERN"]
    = get_variable ("DEPEND_EXTRA_SED_PATTERN",
                    %OCTAVE_CONF_DEPEND_EXTRA_SED_PATTERN%);

  vars["DL_LDFLAGS"] = get_variable ("DL_LDFLAGS",
                                     %OCTAVE_CONF_MKOCTFILE_DL_LDFLAGS%);

  vars["RDYNAMIC_FLAG"] = get_variable ("RDYNAMIC_FLAG",
                                        %OCTAVE_CONF_RDYNAMIC_FLAG%);

  vars["LIBOCTAVE"] = "-loctave";

  vars["LIBOCTINTERP"] = "-loctinterp";

  vars["READLINE_LIBS"] = "-lreadline";

  vars["LAPACK_LIBS"] = get_variable ("LAPACK_LIBS", %OCTAVE_CONF_LAPACK_LIBS%);

  vars["BLAS_LIBS"] = get_variable ("BLAS_LIBS", %OCTAVE_CONF_BLAS_LIBS%);

  vars["FFTW3_LDFLAGS"] = get_variable ("FFTW3_LDFLAGS",
                                        %OCTAVE_CONF_FFTW3_LDFLAGS%);

  vars["FFTW3_LIBS"] = get_variable ("FFTW3_LIBS", %OCTAVE_CONF_FFTW3_LIBS%);

  vars["FFTW3F_LDFLAGS"] = get_variable ("FFTW3F_LDFLAGS",
                                         %OCTAVE_CONF_FFTW3F_LDFLAGS%);

  vars["FFTW3F_LIBS"] = get_variable ("FFTW3F_LIBS", %OCTAVE_CONF_FFTW3F_LIBS%);

  vars["LIBS"] = get_variable ("LIBS", %OCTAVE_CONF_LIBS%);

  vars["FLIBS"] = get_variable ("FLIBS", %OCTAVE_CONF_FLIBS%);

  vars["OCTAVE_LINK_DEPS"] = get_variable ("OCTAVE_LINK_DEPS",
                                           %OCTAVE_CONF_MKOCTFILE_OCTAVE_LINK_DEPS%);

  vars["OCTAVE_LINK_OPTS"] = get_variable ("OCTAVE_LINK_OPTS",
                                           %OCTAVE_CONF_OCTAVE_LINK_OPTS%);

  vars["OCT_LINK_DEPS"] = get_variable ("OCT_LINK_DEPS",
                                        %OCTAVE_CONF_MKOCTFILE_OCT_LINK_DEPS%);

  vars["OCT_LINK_OPTS"] = get_variable ("OCT_LINK_OPTS",
                                        %OCTAVE_CONF_OCT_LINK_OPTS%);

  vars["LDFLAGS"] = get_variable ("LDFLAGS", %OCTAVE_CONF_LDFLAGS%);

  vars["LD_STATIC_FLAG"] = get_variable ("LD_STATIC_FLAG",
                                         %OCTAVE_CONF_LD_STATIC_FLAG%);

  // FIXME: Remove LFLAGS in Octave 7.0
  vars["LFLAGS"] = get_variable ("LFLAGS", DEFAULT_LDFLAGS);
  if (vars["LFLAGS"] != DEFAULT_LDFLAGS)
    std::cerr << "warning: LFLAGS is deprecated and will be removed in a future version of Octave, use LDFLAGS instead" << std::endl;

  vars["F77_INTEGER8_FLAG"] = get_variable ("F77_INTEGER8_FLAG",
                                            %OCTAVE_CONF_F77_INTEGER_8_FLAG%);
  vars["ALL_FFLAGS"] = vars["FFLAGS"] + ' ' + vars["F77_INTEGER8_FLAG"];

  vars["ALL_CFLAGS"]
    = vars["INCFLAGS"] + ' ' + vars["XTRA_CFLAGS"] + ' ' + vars["CFLAGS"];

  vars["ALL_CXXFLAGS"]
    = vars["INCFLAGS"] + ' ' + vars["XTRA_CXXFLAGS"] + ' ' + vars["CXXFLAGS"];

  vars["ALL_LDFLAGS"]
    = vars["LD_STATIC_FLAG"] + ' ' + vars["CPICFLAG"] + ' ' + vars["LDFLAGS"];

  vars["OCTAVE_LIBS"]
    = (vars["LIBOCTINTERP"] + ' ' + vars["LIBOCTAVE"] + ' '
       + vars["SPECIAL_MATH_LIB"]);

  vars["FFTW_LIBS"] = vars["FFTW3_LDFLAGS"] + ' ' + vars["FFTW3_LIBS"] + ' '
                      + vars["FFTW3F_LDFLAGS"] + ' ' + vars["FFTW3F_LIBS"];
}

static std::string usage_msg = "usage: mkoctfile [options] file ...";

static std::string version_msg = "mkoctfile, version " OCTAVE_VERSION;

static bool debug = false;

static std::string help_msg =
  "\n"
  "Options:\n"
  "\n"
  "  -h, -?, --help          Print this message.\n"
  "\n"
  "  -IDIR                   Add -IDIR to compile commands.\n"
  "\n"
  "  -idirafter DIR          Add -idirafter DIR to compile commands.\n"
  "\n"
  "  -DDEF                   Add -DDEF to compile commands.\n"
  "\n"
  "  -lLIB                   Add library LIB to link command.\n"
  "\n"
  "  -LDIR                   Add -LDIR to link command.\n"
  "\n"
  "  -M, --depend            Generate dependency files (.d) for C and C++\n"
  "                          source files.\n"
#if ! defined (OCTAVE_USE_WINDOWS_API)
  "\n"
  "  -pthread                Add -pthread to link command.\n"
#endif
  "\n"
  "  -RDIR                   Add -RDIR to link command.\n"
  "\n"
  "  -Wl,...                 Pass flags though the linker like -Wl,-rpath=...\n"
  "\n"
  "  -W...                   Pass flags though the compiler like -Wa,OPTION.\n"
  "\n"
  "  -c, --compile           Compile, but do not link.\n"
  "\n"
  "  -o FILE, --output FILE  Output filename.  Default extension is .oct\n"
  "                          (or .mex if --mex is specified) unless linking\n"
  "                          a stand-alone executable.\n"
  "\n"
  "  -g                      Enable debugging options for compilers.\n"
  "\n"
  "  -p VAR, --print VAR     Print configuration variable VAR.  There are\n"
  "                          three categories of variables:\n"
  "\n"
  "                          Octave configuration variables that users may\n"
  "                          override with environment variables.  These are\n"
  "                          used in commands that mkoctfile executes.\n"
  "\n"
  "                            ALL_CFLAGS                  INCLUDEDIR\n"
  "                            ALL_CXXFLAGS                LAPACK_LIBS\n"
  "                            ALL_FFLAGS                  LDFLAGS\n"
  "                            ALL_LDFLAGS                 LD_STATIC_FLAG\n"
  "                            BLAS_LIBS                   LIBDIR\n"
  "                            CC                          LIBOCTAVE\n"
  "                            CFLAGS                      LIBOCTINTERP\n"
  "                            CPICFLAG                    OCTAVE_LINK_OPTS\n"
  "                            CPPFLAGS                    OCTINCLUDEDIR\n"
  "                            CXX                         OCTAVE_LIBS\n"
  "                            CXXFLAGS                    OCTAVE_LINK_DEPS\n"
  "                            CXXLD                       OCTLIBDIR\n"
  "                            CXXPICFLAG                  OCT_LINK_DEPS\n"
  "                            DL_LDFLAGS                  OCT_LINK_OPTS\n"
  "                            F77                         RDYNAMIC_FLAG\n"
  "                            F77_INTEGER8_FLAG           SPECIAL_MATH_LIB\n"
  "                            FFLAGS                      XTRA_CFLAGS\n"
  "                            FPICFLAG                    XTRA_CXXFLAGS\n"
  "                            INCFLAGS\n"
  "\n"
  "                          Octave configuration variables as above, but\n"
  "                          currently unused by mkoctfile.\n"
  "\n"
  "                            AR\n"
  "                            DEPEND_EXTRA_SED_PATTERN\n"
  "                            DEPEND_FLAGS\n"
  "                            FFTW3F_LDFLAGS\n"
  "                            FFTW3F_LIBS\n"
  "                            FFTW3_LDFLAGS\n"
  "                            FFTW3_LIBS\n"
  "                            FFTW_LIBS\n"
  "                            FLIBS\n"
  "                            LIBS\n"
  "                            RANLIB\n"
  "                            READLINE_LIBS\n"
  "\n"
  "                          Octave configuration variables that are provided\n"
  "                          for informational purposes only.  Except for\n"
  "                          OCTAVE_HOME and OCTAVE_EXEC_HOME, users may not\n"
  "                          override these variables.\n"
  "\n"
  "                          If OCTAVE_HOME or OCTAVE_EXEC_HOME are set in\n"
  "                          the environment, then other variables are adjusted\n"
  "                          accordingly with OCTAVE_HOME or OCTAVE_EXEC_HOME\n"
  "                          substituted for the original value of the directory\n"
  "                          specified by the --prefix or --exec-prefix options\n"
  "                          that were used when Octave was configured.\n"
  "\n"
  "                            API_VERSION                 LOCALFCNFILEDIR\n"
  "                            ARCHLIBDIR                  LOCALOCTFILEDIR\n"
  "                            BINDIR                      LOCALSTARTUPFILEDIR\n"
  "                            CANONICAL_HOST_TYPE         LOCALVERARCHLIBDIR\n"
  "                            DATADIR                     LOCALVERFCNFILEDIR\n"
  "                            DATAROOTDIR                 LOCALVEROCTFILEDIR\n"
  "                            DEFAULT_PAGER               MAN1DIR\n"
  "                            EXEC_PREFIX                 MAN1EXT\n"
  "                            EXEEXT                      MANDIR\n"
  "                            FCNFILEDIR                  OCTAVE_EXEC_HOME\n"
  "                            IMAGEDIR                    OCTAVE_HOME\n"
  "                            INFODIR                     OCTAVE_VERSION\n"
  "                            INFOFILE                    OCTDATADIR\n"
  "                            LIBEXECDIR                  OCTDOCDIR\n"
  "                            LOCALAPIARCHLIBDIR          OCTFILEDIR\n"
  "                            LOCALAPIFCNFILEDIR          OCTFONTSDIR\n"
  "                            LOCALAPIOCTFILEDIR          STARTUPFILEDIR\n"
  "                            LOCALARCHLIBDIR\n"
  "\n"
  "  --link-stand-alone      Link a stand-alone executable file.\n"
  "\n"
  "  --mex                   Assume we are creating a MEX file.  Set the\n"
  "                          default output extension to \".mex\".\n"
  "\n"
  "  -s, --strip             Strip output file.\n"
  "\n"
  "  -n, --just-print, --dry-run\n"
  "                          Print commands, but do not execute them.\n"
  "\n"
  "  -v, --verbose           Echo commands as they are executed.\n"
  "\n"
  "  --silent                Ignored.  Intended to suppress output from\n"
  "                          compiler steps.\n"
  "\n"
  "  FILE                    Compile or link FILE.  Recognized file types are:\n"
  "\n"
  "                            .c    C source\n"
  "                            .cc   C++ source\n"
  "                            .cp   C++ source\n"
  "                            .cpp  C++ source\n"
  "                            .CPP  C++ source\n"
  "                            .cxx  C++ source\n"
  "                            .c++  C++ source\n"
  "                            .C    C++ source\n"
  "                            .f    Fortran source (fixed form)\n"
  "                            .F    Fortran source (fixed form)\n"
  "                            .f90  Fortran source (free form)\n"
  "                            .F90  Fortran source (free form)\n"
  "                            .o    object file\n"
  "                            .a    library file\n"
#if defined (_MSC_VER)
  "                            .lib  library file\n"
#endif
  "\n";

static std::string
basename (const std::string& s, bool strip_path = false)
{
  std::string retval;

  size_t pos = s.rfind ('.');

  if (pos == std::string::npos)
    retval = s;
  else
    retval = s.substr (0, pos);

  if (strip_path)
    {
      size_t p1 = retval.rfind ('/'), p2 = retval.rfind ('\\');

      pos = (p1 != std::string::npos && p2 != std::string::npos
             ? std::max (p1, p2) : (p2 != std::string::npos ? p2 : p1));

      if (pos != std::string::npos)
        retval = retval.substr (++pos, std::string::npos);
    }

  return retval;
}

inline bool
starts_with (const std::string& s, const std::string& prefix)
{
  return (s.length () >= prefix.length () && s.find (prefix) == 0);
}

inline bool
ends_with (const std::string& s, const std::string& suffix)
{
  return (s.length () >= suffix.length ()
          && s.rfind (suffix) == s.length () - suffix.length ());
}

static int
run_command (const std::string& cmd, bool printonly = false)
{
  if (printonly)
    {
      std::cout << cmd << std::endl;
      return 0;
    }

  if (debug)
    std::cout << cmd << std::endl;

  int result = system (cmd.c_str ());

  if (octave_wifexited_wrapper (result))
    result = octave_wexitstatus_wrapper (result);

  return result;
}

bool
is_true (const std::string& s)
{
  return (s == "yes" || s == "true");
}

static std::string
get_temp_directory (void)
{
  std::string tempd;

  tempd = octave_getenv ("TMPDIR");

#if defined (__MINGW32__) || defined (_MSC_VER)

  if (tempd.empty ())
    tempd = octave_getenv ("TEMP");

  if (tempd.empty ())
    tempd = octave_getenv ("TMP");

#if defined (P_tmpdir)
  if (tempd.empty ())
    tempd = P_tmpdir;
#endif

  // Some versions of MinGW and MSVC either don't define P_tmpdir, or
  // define it to a single backslash.  In such cases just use C:\temp.
  if (tempd.empty () || tempd == R"(\)")
    tempd = R"(c:\temp)";

#else

#if defined (P_tmpdir)
  if (tempd.empty ())
    tempd = P_tmpdir;
#else
  if (tempd.empty ())
    tempd = "/tmp";
#endif

#endif

  return tempd;
}

static std::string
create_interleaved_complex_file (void)
{
  std::string tmpl = get_temp_directory () + "/oct-XXXXXX.c";

  char *ctmpl = new char [tmpl.length () + 1];

  ctmpl = strcpy (ctmpl, tmpl.c_str ());

  // mkostemps will open the file and return a file descriptor.  We
  // won't worry about closing it because we will need the file until we
  // are done and then the file will be closed when mkoctfile exits.
  int fd = octave_mkostemps_wrapper (ctmpl, 2);

  // Make C++ string from filled-in template.
  std::string retval (ctmpl);
  delete [] ctmpl;

  // Write symbol definition to file.
  FILE *fid = fdopen (fd, "w");
  fputs ("const int __mx_has_interleaved_complex__ = 1;\n", fid);
  fclose (fid);

  return retval;
}

static std::string
tmp_objfile_name (void)
{
  std::string tmpl = get_temp_directory () + "/oct-XXXXXX.o";

  char *ctmpl = new char [tmpl.length () + 1];

  ctmpl = strcpy (ctmpl, tmpl.c_str ());

  // mkostemps will open the file and return a file descriptor.  We
  // won't worry about closing it because we will need the file until we
  // are done and then the file will be closed when mkoctfile exits.
  octave_mkostemps_wrapper (ctmpl, 2);

  std::string retval (ctmpl);  // make C++ string from filled-in template
  delete [] ctmpl;

  return retval;
}

static void
clean_up_tmp_files (const std::list<std::string>& tmp_files)
{
  for (const auto& file : tmp_files)
    octave_unlink_wrapper (file.c_str ());
}

int
main (int argc, char **argv)
{
  initialize ();

  if (argc == 1)
    {
      std::cout << usage_msg << std::endl;
      return 1;
    }

  if (argc == 2 && (! strcmp (argv[1], "-v")
                    || ! strcmp (argv[1], "-version")
                    || ! strcmp (argv[1], "--version")))
    {
      std::cout << version_msg << std::endl;
      return 0;
    }

  std::list<std::string> cfiles, ccfiles, f77files, tmp_objfiles;
  std::string output_ext = ".oct";
  std::string objfiles, libfiles, octfile, outputfile;
  std::string incflags, defs, ldflags, pass_on_options;
  bool strip = false;
  bool no_oct_file_strip_on_this_platform = is_true ("%NO_OCT_FILE_STRIP%");
  bool compile_only = false;
  bool link_stand_alone = false;
  bool depend = false;
  bool printonly = false;
  bool output_file_option = false;
  bool creating_mex_file = false;
  bool r2017b_option = false;
  bool r2018a_option = false;
  // The default for this may change in the future.
  bool mx_has_interleaved_complex = false;

  for (int i = 1; i < argc; i++)
    {
      std::string arg = argv[i];

      std::string file;

      if (ends_with (arg, ".c"))
        {
          file = arg;
          cfiles.push_back (file);
        }
      else if (ends_with (arg, ".cc") || ends_with (arg, ".cp")
               || ends_with (arg, ".cpp") || ends_with (arg, ".CPP")
               || ends_with (arg, ".cxx") || ends_with (arg, ".c++")
               || ends_with (arg, ".C"))
        {
          file = arg;
          ccfiles.push_back (file);
        }
      else if (ends_with (arg, ".f") || ends_with (arg, ".F")
               || ends_with (arg, "f90") || ends_with (arg, ".F90"))
        {
          file = arg;
          f77files.push_back (file);
        }
      else if (ends_with (arg, ".o") || ends_with (arg, ".obj"))
        {
          file = arg;
          objfiles += (' ' + quote_path (arg));
        }
      else if (ends_with (arg, ".lib") || ends_with (arg, ".a"))
        {
          file = arg;
          libfiles += (' ' + quote_path (arg));
        }
      else if (arg == "-d" || arg == "-debug" || arg == "--debug"
               || arg == "-v" || arg == "-verbose" ||  arg == "--verbose")
        {
          debug = true;
          if (vars["CC"] == "cc-msvc")
            vars["CC"] += " -d";
          if (vars["CXX"] == "cc-msvc")
            vars["CXX"] += " -d";
          if (vars["CXXLD"] == "cc-msvc")
            vars["CXXLD"] += " -d";
        }
      else if (arg == "-silent" ||  arg == "--silent")
        {
          // Ignored for now.
        }
      else if (arg == "-h" || arg == "-?" || arg == "-help" || arg == "--help")
        {
          std::cout << usage_msg << std::endl;
          std::cout << help_msg << std::endl;
          return 0;
        }
      else if (starts_with (arg, "-I"))
        {
          incflags += (' ' + quote_path (arg));
        }
      else if (arg == "-idirafter")
        {
          if (i < argc-1)
            {
              arg = argv[++i];
              incflags += (" -idirafter " + arg);
            }
          else
            std::cerr << "mkoctfile: include directory name missing"
                      << std::endl;
        }
      else if (starts_with (arg, "-D"))
        {
          defs += (' ' + arg);
        }
      else if (arg == "-largeArrayDims" || arg == "-compatibleArrayDims")
        {
          std::cerr << "warning: -largeArrayDims and -compatibleArrayDims are accepted for compatibility, but ignored" << std::endl;
        }
      else if (arg == "-R2017b")
        {
          if (r2018a_option)
            {
              std::cerr << "mkoctfile: only one of -R2017b and -R2018a may be used" << std::endl;
              return 1;
            }

          r2017b_option = true;
        }
      else if (arg == "-R2018a")
        {
          if (r2017b_option)
            {
              std::cerr << "mkoctfile: only one of -R2017b and -R2018a may be used" << std::endl;
              return 1;
            }

          r2018a_option = true;
          mx_has_interleaved_complex = true;
        }
      else if (starts_with (arg, "-Wl,") || starts_with (arg, "-l")
               || starts_with (arg, "-L") || starts_with (arg, "-R"))
        {
          ldflags += (' ' + quote_path (arg));
        }
#if ! defined (OCTAVE_USE_WINDOWS_API)
      else if (arg == "-pthread")
        {
          ldflags += (' ' + arg);
        }
#endif
      else if (arg == "-M" || arg == "-depend" || arg == "--depend")
        {
          depend = true;
        }
      else if (arg == "-o" || arg == "-output" || arg == "--output")
        {
          output_file_option = true;

          if (i < argc-1)
            {
              arg = argv[++i];
              outputfile = arg;
            }
          else
            std::cerr << "mkoctfile: output filename missing" << std::endl;
        }
      else if (arg == "-n" || arg == "--dry-run" || arg == "--just-print")
        {
          printonly = true;
        }
      else if (arg == "-p" || arg == "-print" || arg == "--print")
        {
          if (i < argc-1)
            {
              arg = argv[++i];
              // FIXME: Remove LFLAGS checking in Octave 7.0
              if (arg == "LFLAGS")
                std::cerr << "warning: LFLAGS is deprecated and will be removed in a future version of Octave, use LDFLAGS instead" << std::endl;

              std::cout << vars[arg] << std::endl;
              return 0;
            }
          else
            std::cerr << "mkoctfile: --print requires argument" << std::endl;
        }
      else if (arg == "-s" || arg == "-strip" || arg == "--strip")
        {
          if (no_oct_file_strip_on_this_platform)
            std::cerr << "mkoctfile: stripping disabled on this platform"
                      << std::endl;
          else
            strip = true;
        }
      else if (arg == "-c" || arg == "-compile" || arg == "--compile")
        {
          compile_only = true;
        }
      else if (arg == "-g")
        {
          vars["ALL_CFLAGS"] += " -g";
          vars["ALL_CXXFLAGS"] += " -g";
          vars["ALL_FFLAGS"] += " -g";
        }
      else if (arg == "-link-stand-alone" || arg == "--link-stand-alone")
        {
          link_stand_alone = true;
        }
      else if (arg == "-mex" || arg == "--mex")
        {
          creating_mex_file = true;

          incflags += " -I.";
#if defined (_MSC_VER)
          ldflags += " -Wl,-export:mexFunction";
#endif
          output_ext = ".mex";
        }
      else if (starts_with (arg, "-W"))
        {
          pass_on_options += (' ' + arg);
        }
      else if (starts_with (arg, "-O"))
        {
          pass_on_options += (' ' + arg);
        }
      else if (starts_with (arg, "-"))
        {
          // Pass through any unrecognized options.
          pass_on_options += (' ' + arg);
          // Check for an additional argument following the option.
          // However, don't check the final position which is typically a file
          if (i < argc-2)
            {
              arg = argv[i+1];
              if (arg[0] != '-')
                {
                  pass_on_options += (' ' + arg);
                  i++;
                }
            }
        }
      else
        {
          std::cerr << "mkoctfile: unrecognized argument " << arg << std::endl;
          return 1;
        }

      if (! file.empty () && octfile.empty ())
        octfile = file;
    }

  if (creating_mex_file)
    {
      if (vars["ALL_CFLAGS"].find ("-g") != std::string::npos)
        defs += " -DMEX_DEBUG";

      if (mx_has_interleaved_complex)
        {
          defs += " -DMX_HAS_INTERLEAVED_COMPLEX=1";

          if (! compile_only)
            {
              // Create tmp C source file that defines an extern symbol
              // that can be checked when loading the mex file to
              // determine that the file was compiled expecting
              // interleaved complex values.

              std::string tmp_file = create_interleaved_complex_file ();

              cfiles.push_back (tmp_file);
            }
        }
    }
  else
    {
      if (r2017b_option)
        std::cerr << "warning: -R2017b option ignored unless creating mex file"
                  << std::endl;

      if (r2018a_option)
        std::cerr << "warning: -R2018a option ignored unless creating mex file"
                  << std::endl;
    }

  if (compile_only && output_file_option
      && (cfiles.size () + ccfiles.size () + f77files.size ()) > 1)
    {
      std::cerr << "mkoctfile: may not use -c and -o with multiple source files"
                << std::endl;
      return 1;
    }

  std::string output_option;

  if (link_stand_alone)
    {
      if (! outputfile.empty ())
        output_option = "-o " + outputfile;
    }
  else
    {
      if (! outputfile.empty ())
        {
          // FIXME: should probably do a better job of finding the
          // filename extension instead of just looking at the filename
          // length.

          octfile = outputfile;
          size_t len = octfile.length ();
          size_t len_ext = output_ext.length ();
          if (len <= len_ext || octfile.substr (len-len_ext) != output_ext)
            octfile += output_ext;
        }
      else
        octfile = basename (octfile, true) + output_ext;
    }

  if (depend)
    {
      for (const auto& f : cfiles)
        {
          std::string dfile = basename (f, true) + ".d", line;

          octave_unlink_wrapper (dfile.c_str ());

          std::string cmd
            = (vars["CC"] + ' ' + vars["DEPEND_FLAGS"] + ' '
               + vars["CPPFLAGS"] + ' ' + vars["ALL_CFLAGS"] + ' '
               + incflags  + ' ' + defs + ' ' + quote_path (f));

          FILE *fd = popen (cmd.c_str (), "r");
          std::ofstream fo (dfile.c_str ());
          size_t pos;
          while (! feof (fd))
            {
              line = get_line (fd);
              if ((pos = line.rfind (".o:")) != std::string::npos)
                {
                  size_t spos = line.rfind ('/', pos);
                  std::string ofile
                    = (spos == std::string::npos
                       ? line.substr (0, pos+2)
                       : line.substr (spos+1, pos-spos+1));
                  fo << "pic/" << ofile << ' ' << ofile << ' '
                     << dfile << line.substr (pos) << std::endl;
                }
              else
                fo << line << std::endl;
            }
          pclose (fd);
          fo.close ();
        }

      for (const auto& f : ccfiles)
        {
          std::string dfile = basename (f, true) + ".d", line;

          octave_unlink_wrapper (dfile.c_str ());

          std::string cmd
            = (vars["CXX"] + ' ' + vars["DEPEND_FLAGS"] + ' '
               + vars["CPPFLAGS"] + ' ' + vars["ALL_CXXFLAGS"] + ' '
               + incflags  + ' ' + defs + ' ' + quote_path (f));

          FILE *fd = popen (cmd.c_str (), "r");
          std::ofstream fo (dfile.c_str ());
          size_t pos;
          while (! feof (fd))
            {
              line = get_line (fd);
              if ((pos = line.rfind (".o:")) != std::string::npos)
                {
                  size_t spos = line.rfind ('/', pos);
                  std::string ofile
                    = (spos == std::string::npos
                       ? line.substr (0, pos+2)
                       : line.substr (spos+1, pos-spos+1));
                  fo << "pic/" << ofile << ' ' << ofile << ' '
                     << dfile << line.substr (pos+2) << std::endl;
                }
              else
                fo << line << std::endl;
            }
          pclose (fd);
          fo.close ();
        }

      return 0;
    }

  for (const auto& f : f77files)
    {
      if (! vars["F77"].empty ())
        {
          std::string o;
          if (compile_only)
            {
              if (! outputfile.empty ())
                o = outputfile;
              else
                o = basename (f, true) + ".o";
            }
          else
            {
              o = tmp_objfile_name ();

              tmp_objfiles.push_back (o);

              objfiles += (' ' + o);
            }

          std::string cmd
            = (vars["F77"] + " -c " + vars["FPICFLAG"] + ' '
               + vars["ALL_FFLAGS"] + ' ' + incflags + ' ' + defs + ' '
               + pass_on_options + ' ' + f + " -o " + o);

          int status = run_command (cmd, printonly);

          if (status)
            return status;
        }
      else
        {
          std::cerr << "mkoctfile: no way to compile Fortran file " << f
                    << std::endl;
          return 1;
        }
    }

  for (const auto& f : cfiles)
    {
      if (! vars["CC"].empty ())
        {
          std::string o;
          if (compile_only)
            {
              if (! outputfile.empty ())
                o = outputfile;
              else
                o = basename (f, true) + ".o";
            }
          else
            {
              o = tmp_objfile_name ();

              tmp_objfiles.push_back (o);

              objfiles += (' ' + o);
            }

          std::string cmd
            = (vars["CC"] + " -c " + vars["CPPFLAGS"] + ' '
               + vars["CPICFLAG"] + ' ' + vars["ALL_CFLAGS"] + ' '
               + pass_on_options + ' ' + incflags + ' ' + defs + ' '
               + quote_path (f) + " -o " + quote_path (o));

          int status = run_command (cmd, printonly);

          if (status)
            return status;
        }
      else
        {
          std::cerr << "mkoctfile: no way to compile C file " << f
                    << std::endl;
          return 1;
        }
    }

  for (const auto& f : ccfiles)
    {
      if (! vars["CXX"].empty ())
        {
          std::string o;
          if (compile_only)
            {
              if (! outputfile.empty ())
                o = outputfile;
              else
                o = basename (f, true) + ".o";
            }
          else
            {
              o = tmp_objfile_name ();

              tmp_objfiles.push_back (o);

              objfiles += (' ' + o);
            }

          std::string cmd
            = (vars["CXX"] + " -c " + vars["CPPFLAGS"] + ' '
               + vars["CXXPICFLAG"] + ' ' + vars["ALL_CXXFLAGS"] + ' '
               + pass_on_options + ' ' + incflags + ' ' + defs + ' '
               + quote_path (f) + " -o " + quote_path (o));

          int status = run_command (cmd, printonly);

          if (status)
            return status;
        }
      else
        {
          std::cerr << "mkoctfile: no way to compile C++ file " << f
                    << std::endl;
          return 1;
        }
    }

  // If we are only compiling, we are done.

  if (compile_only)
    return 0;

  if (objfiles.empty ())
    {
      std::cerr << "mkoctfile: no objects to link" << std::endl;
      return 1;
    }

  std::string octave_libs;

  if (link_stand_alone)
    {
      if (! vars["CXXLD"].empty ())
        {
          octave_libs = "-L" + quote_path (vars["OCTLIBDIR"])
                        + ' ' + vars["OCTAVE_LIBS"];

          std::string cmd
            = (vars["CXXLD"] + ' ' + vars["CPPFLAGS"] + ' '
               + vars["ALL_CXXFLAGS"] + ' ' + vars["RDYNAMIC_FLAG"] + ' '
               + vars["ALL_LDFLAGS"] + ' ' + pass_on_options + ' '
               + output_option + ' ' + objfiles + ' ' + libfiles + ' '
               + ldflags + ' ' + vars["LFLAGS"] + ' ' + octave_libs + ' '
               + vars["OCTAVE_LINK_OPTS"] + ' ' + vars["OCTAVE_LINK_DEPS"]);

          int status = run_command (cmd, printonly);

          clean_up_tmp_files (tmp_objfiles);

          if (status)
            return status;
        }
      else
        {
          std::cerr
            << "mkoctfile: no way to link stand-alone executable file"
            << std::endl;
          return 1;
        }
    }
  else
    {
#if defined (OCTAVE_USE_WINDOWS_API) || defined(CROSS)
      octave_libs = "-L" + quote_path (vars["OCTLIBDIR"])
                    + ' ' + vars["OCTAVE_LIBS"];
#endif

      std::string cmd
        = (vars["CXXLD"] + ' ' + vars["ALL_CXXFLAGS"] + ' '
           + vars["DL_LDFLAGS"] + ' ' + vars["LDFLAGS"] + ' ' + pass_on_options
           + " -o " + octfile + ' ' + objfiles + ' ' + libfiles + ' '
           + ldflags + ' ' + vars["LFLAGS"] + ' ' + octave_libs + ' '
           + vars["OCT_LINK_OPTS"] + ' ' + vars["OCT_LINK_DEPS"]);

#if defined (OCTAVE_USE_WINDOWS_API) || defined(CROSS)
      if (! f77files.empty () && ! vars["FLIBS"].empty ())
        cmd += ' ' + vars["FLIBS"];
#endif

      int status = run_command (cmd, printonly);

      clean_up_tmp_files (tmp_objfiles);

      if (status)
        return status;
    }

  if (strip)
    {
      std::string cmd = "strip " + octfile;

      int status = run_command (cmd, printonly);

      if (status)
        return status;
    }

  return 0;
}