[m-rev.] for review: implement C version of mmc

Ian MacLarty maclarty at cs.mu.OZ.AU
Sat Oct 1 18:51:38 AEST 2005


For review by anyone.

Estimated hours taken: 30
Branches: main and 0.12

Implement a C version of mmc that can be run on Windows without a unix shell.

The new mmc can also operate in a standalone mode where it determines where it
is being run from, allowing it to still work even if the installed compiler is
moved to a new directory.  This mode will be used for the upcoming Windows
installer.

Add some configure options which turn on the stanalone mode.  In the standalone
mode relative paths to the C compiler and linker must also be supplied.  These
are assumed to be packaged with the Mercury compiler in standalone mode.  mmc
will convert the relative paths to absolute paths at runtime and pass these
to the Mercury compiler.

In non-stanalone mode the C version of mmc behaves exactly the same as the
shell script version.  The C version is now the version which is installed to
the bin directory, however the shell version is still used in the compiler
build process and for bootchecking.

Fix some unrelated C compiler warnings (these changes are very short).

README.MS-Windows:
	Add a reference to README.Standalone.

README.Standalone:
	Add a description of how to package the Mercury compiler and C
	compiler up in a standalone package.
	Include an example of how to package the Mercury compiler up with
	MinGW, so it is independent of Cygwin.

configure.in:
	Add options to enable the standalone version of mmc and to give
	relative paths for the C compiler and the linker commands that generate
	an executable and a shared library.  If standalone mode is enabled then
	all three relative paths must be present.  If standalone mode is not
	enabled, then the relative paths are ignored.

	Set the configure variable that determines whether the
	MACOSX_DEPLOYMENT_TARGET environment variable should be set, to 1 or 0,
	instead of setting it to the actual shell command which sets the
	environment variable.  This is done so that the configure variable
	can be checked in both the C and sh versions of mmc.

	Build util/mmc.c.

library/dir.m:
library/io.m:
	Fix two C compiler warnings.

scripts/Mmakefile:
	Do not install the sh version of mmc.

scripts/ml.in:
scripts/mmake.in:
scripts/mmc.in:
	Set MACOSX_DEPLOYMENT_TARGET if necessary.

util/Mmakefile:
	Build the C version of mmc.  It has different rules to the
	other C files in util, because it links in path_util.

util/mmc.c.in:
	The C version of mmc.

util/path_util.c:
util/path_util.h:
	Utility functions used by mmc to determine where it is being executed
	from.

Index: README.MS-Windows
===================================================================
RCS file: /home/mercury1/repository/mercury/README.MS-Windows,v
retrieving revision 1.19
diff -u -r1.19 README.MS-Windows
--- README.MS-Windows	30 May 2004 22:59:33 -0000	1.19
+++ README.MS-Windows	1 Oct 2005 08:30:41 -0000
@@ -175,4 +175,9 @@
         mdemangle.exe (in <prefix>/bin)
         mkinit.exe (in <prefix>/bin)

+The Mercury compiler built using the above process will still depend on cygwin,
+but the executables it generates will not.  It is also possible to build a
+version of the compiler which is completely independent of Cygwin.  For details
+on how to build such a compiler see README.Standalone.
+
 -----------------------------------------------------------------------------
Index: README.Standalone
===================================================================
RCS file: README.Standalone
diff -N README.Standalone
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ README.Standalone	1 Oct 2005 08:27:44 -0000
@@ -0,0 +1,94 @@
+-----------------------------------------------------------------------------
+
+INTRODUCTION
+
+This file describes how to build a version of the Mercury compiler that has
+a C compiler and linker packaged with it and so doesn't depend on any external
+programs.  The directory containing the Mercury and C compilers can be moved
+around and mmc will still work (although at this stage mmake, mdb, etc will
+not).  This property makes the standalone version of the compiler ideal for
+distribution in a binary package.
+
+-----------------------------------------------------------------------------
+
+OVERVIEW
+
+mmc is a wrapper which calls the real Mercury compiler executable,
+mercury_compile.  mmc can operate in one of two modes: standalone mode or
+non-standalone mode.
+
+In non-standalone mode the paths to the Mercury compiler, the C compiler and
+the linker are all resolved at configuration time.  This means that once the
+compiler is installed, it cannot be moved without reconfiguring.
+
+In standalone mode relative paths to the C compiler and linker are supplied
+at configuration time.  mmc then resolves the absolute paths to the C compiler
+and linker each time it is run and passes these to the Mercury compiler.
+This means that once the Mercury compiler is installed, the installation
+directory can be moved and mmc will still work as long as the C compiler
+and linker are still in the same relative path.
+
+-----------------------------------------------------------------------------
+
+CONFIGURATION
+
+To enable mmc to operate in standalone mode, four options must be given to
+the configure script.
+
+1. --enable-standalone
+	This causes mmc to be compiled in standalone mode.
+2. --with-relative-cc=<relative path to C compiler>
+	This specifies the relative command to invoke the C compiler.  The
+	command should be relative to the installation directory (i.e. the
+	directory given with the --prefix configure option).
+3. --with-relative-link-exec-cmd=<relative path to link command>
+	This specifies the relative path to the command to invoke the linker to
+	generate an executable.
+4. --with-relative-link-shared-cmd=<relative path to link command>
+	This specifies the relative path to the command to invoke the linker to
+	generate a shared library.
+
+-----------------------------------------------------------------------------
+
+BUILDING
+
+After configuration the compiler can be built and installed as usual
+(using "make" and "make install" with the source distribution or
+"mmake" and "mmake install" with the CVS version).
+
+After installation the C compiler and linker as well as all other files
+needed by the C compiler and linker should be copied to the correct
+location relative to the installed Mercury compiler directory.  All these
+files can then be packaged up and moved around and mmc will continue to
+work as long as it is in the PATH.
+
+-----------------------------------------------------------------------------
+
+MINGW EXAMPLE
+
+As an example here is how to build a standalone Windows version of the Mercury
+compiler that comes packaged with MinGW and so doesn't need any other
+external programs.
+
+First you'll need to download and install Cygwin and MinGW.
+Next download the Mercury source distribution and unpack it in Cygwin.
+In Cygwin, cd into the source distribution directory and do:
+
+./configure --with-cc=/cygdrive/c/mingw/bin/gcc.exe --prefix=<install_dir> --enable-standalone --with-relative-cc=mingw\\\\bin\\\\gcc.exe --with-relative-link-exec-cmd=mingw\\\\bin\\\\gcc.exe --with-relative-link-shared-cmd=mingw\\\\bin\\\\gcc.exe
+
+make
+
+make install
+
+The above assumes that MinGW is installed in c:\mingw.
+Note that the \ characters must be double escaped: once for the shell and
+once for the C program they will end up in.
+<install_dir> above is a directory to install the Mercury compiler to
+(the directory will be created if it doesn't exist).
+
+After "make install" has finished copy the c:\mingw directory into
+<install_dir>.
+
+<install_dir> can now be packaged up and copied to another Windows machine
+and mmc should work from the Windows command prompt as long as mmc is in the
+PATH.  Cygwin is not needed on the machine the package is deployed to.
Index: configure.in
===================================================================
RCS file: /home/mercury1/repository/mercury/configure.in,v
retrieving revision 1.434
diff -u -r1.434 configure.in
--- configure.in	23 Sep 2005 06:39:46 -0000	1.434
+++ configure.in	28 Sep 2005 11:13:27 -0000
@@ -314,6 +314,72 @@
 		CC="$mercury_cv_with_cc"
 		;;
 esac
+
+#-----------------------------------------------------------------------------#
+# Check if we should enable the standalone version of mmc.
+AC_ARG_ENABLE(standalone,
+[  --enable-standalone     Enable the standalone version of mmc.  If this
+                          option is enabled then the --with-relative-cc,
+                          --with-relative-link-exec-cmd and
+                          --with-relative-link-shared-cmd options must also
+                          be set.],
+mercury_cv_enable_standalone="yes", mercury_cv_enable_standalone="no")
+AC_ARG_WITH(relative-cc,
+[  --with-relative-cc      Specify the path to the C compiler relative to
+                          the installed Mercury directory.  The given C
+                          compiler is only used if the --enable-standalone
+                          option is given.],
+mercury_cv_with_relative_cc="$withval", mercury_cv_with_relative_cc="")
+AC_ARG_WITH(relative-link-exec-cmd,
+[  --with-relative-link-exec-cmd
+                          Specify the command to link object files into an
+                          executable.  The command is relative to the Mercury
+                          installed directory.  The specified link command
+                          will only be used if the --enable-standalone option
+                          is also given.],
+mercury_cv_with_relative_link_exec_cmd="$withval",
+mercury_cv_with_relative_link_exec_cmd="")
+AC_ARG_WITH(relative-link-shared-cmd,
+[  --with-relative-link-shared-cmd
+                          Specify the command to link object files into a
+                          shared library.  The command is relative to the
+                          Mercury installed directory.  The specified link
+                          command will only be used if the --enable-standalone
+                          option is also given.],
+mercury_cv_with_relative_link_shared_cmd="$withval",
+mercury_cv_with_relative_link_shared_cmd="")
+case "$mercury_cv_enable_standalone" in
+	yes)	case "$mercury_cv_with_relative_cc" in
+			"")	AC_MSG_ERROR(--with-relative-cc is required if --enable-standalone is given)
+				exit 1
+				;;
+			*)	C_COMPILER_RELATIVE_PATH="$mercury_cv_with_relative_cc"
+				AC_SUBST(C_COMPILER_RELATIVE_PATH)
+				;;
+		esac
+		case "$mercury_cv_with_relative_link_exec_cmd" in
+			"")	AC_MSG_ERROR(--with-relative-link-exec-cmd is required if --enable-standalone is given)
+				exit 1
+				;;
+			*)	LINK_EXEC_CMD_RELATIVE_PATH="$mercury_cv_with_relative_link_exec_cmd"
+				AC_SUBST(LINK_EXEC_CMD_RELATIVE_PATH)
+				;;
+		esac
+		case "$mercury_cv_with_relative_link_shared_cmd" in
+			"")	AC_MSG_ERROR(--with-relative-link-shared-cmd is required if --enable-standalone is given)
+				exit 1
+				;;
+			*)	LINK_SHARED_LIB_CMD_RELATIVE_PATH="$mercury_cv_with_relative_link_shared_cmd"
+				AC_SUBST(LINK_SHARED_LIB_CMD_RELATIVE_PATH)
+				;;
+		esac
+		MMC_STANDALONE=1
+		;;
+	no)	MMC_STANDALONE=0
+		;;
+esac
+AC_SUBST(MMC_STANDALONE)
+
 #-----------------------------------------------------------------------------#
 #
 # Find the GCC source code
@@ -3143,6 +3209,13 @@
 ERROR_UNDEFINED=""
 DEFAULT_LINKAGE="shared"

+# On Darwin the MACOSX_DEPLOYMENT_TARGET environment variable needs to be set
+# when linking with two level namespaces so we can use the `-undefined
+# dynamic_lookup' option.  If necessary the configure script will set the
+# following variable to 1 which will cause mmc to set the
+# MACOSX_DEPLOYMENT_TARGET environment variable.
+SET_MACOSX_DEPLOYMENT_TARGET=0
+
 case "$host" in
 	i*86-*-linux|i*86-*-linux-gnu)
 		case $ac_cv_prog_gcc in
@@ -3391,14 +3464,7 @@
 			    -a 7 -le;
 		    then
 		        AC_MSG_RESULT(yes)
-			# The MACOSX_DEPLOYMENT_TARGET environment variable
-			# needs to be set when linking with two level
-			# namespaces so we can use the
-			# `-undefined dynamic_lookup' option.
-		        SET_MACOSX_DEPLOYMENT_TARGET="\
-				MACOSX_DEPLOYMENT_TARGET=10.3; \
-				export MACOSX_DEPLOYMENT_TARGET"
-			AC_SUBST(SET_MACOSX_DEPLOYMENT_TARGET)
+		        SET_MACOSX_DEPLOYMENT_TARGET=1
 		        LINK_SHARED_OBJ="$GCC_PROG -multiply_defined suppress \
 				 -dynamiclib -single_module"
 		        LINK_SHARED_OBJ_SH="$GCC_PROG -multiply_defined \
@@ -3431,6 +3497,7 @@
 		AC_MSG_RESULT(no)
 		;;
 esac
+AC_SUBST(SET_MACOSX_DEPLOYMENT_TARGET)

 #-----------------------------------------------------------------------------#
 # Note that changes here may require changes in scripts/mgnuc.in.
@@ -4149,7 +4216,7 @@
 # if you add new .in files to either of those you *must* update
 # scripts/mercury_config.in.
 #
-AC_OUTPUT(Mmake.common scripts/Mmake.vars scripts/mmc
+AC_OUTPUT(Mmake.common scripts/Mmake.vars scripts/mmc util/mmc.c
 scripts/mercury.bat scripts/mprof scripts/mercury_update_interface
 scripts/mgnuc scripts/parse_ml_options.sh-subr scripts/ml
 scripts/c2init scripts/mmake scripts/mdb scripts/mdbrc scripts/mdprof
Index: library/dir.m
===================================================================
RCS file: /home/mercury1/repository/mercury/library/dir.m,v
retrieving revision 1.26
diff -u -r1.26 dir.m
--- library/dir.m	5 Sep 2005 07:24:10 -0000	1.26
+++ library/dir.m	30 Sep 2005 10:37:40 -0000
@@ -1349,7 +1349,7 @@
         Dir = FindFirstFile(dir_pattern, &file_data);
         if (Dir == INVALID_HANDLE_VALUE) {
             int error = GetLastError();
-            if (error = ERROR_NO_MORE_FILES) {
+            if (error == ERROR_NO_MORE_FILES) {
                 Result = ML_make_dir_open_result_eof();
             } else {
                 ML_make_dir_open_result_error(error, &Result);
Index: library/io.m
===================================================================
RCS file: /home/mercury1/repository/mercury/library/io.m,v
retrieving revision 1.334
diff -u -r1.334 io.m
--- library/io.m	22 Sep 2005 06:17:26 -0000	1.334
+++ library/io.m	30 Sep 2005 10:41:51 -0000
@@ -7635,7 +7635,7 @@
             Status = 127;
         }
     #else
-        if (Status0 & 0xff != 0) {
+        if ((Status0 & 0xff) != 0) {
             /* the process was killed by a signal */
             Status = -(Status0 & 0xff);
         } else {
Index: scripts/Mmakefile
===================================================================
RCS file: /home/mercury1/repository/mercury/scripts/Mmakefile,v
retrieving revision 1.39
diff -u -r1.39 Mmakefile
--- scripts/Mmakefile	25 May 2005 05:19:50 -0000	1.39
+++ scripts/Mmakefile	9 Sep 2005 06:02:09 -0000
@@ -21,7 +21,7 @@
 	mtc \
 	vpath_find

-CONF_SCRIPTS = \
+INSTALL_CONF_SCRIPTS = \
 	c2init \
 	canonical_grade \
 	mdb \
@@ -33,10 +33,16 @@
 	mkfifo_using_mknod \
 	ml \
 	mmake \
-	mmc \
 	mprof \
 	prepare_tmp_dir_fixed_part

+NONINSTALL_CONF_SCRIPTS = \
+	mmc
+
+CONF_SCRIPTS = $(INSTALL_CONF_SCRIPTS) $(NONINSTALL_CONF_SCRIPTS)
+
+INSTALL_SCRIPTS = $(NONCONF_SCRIPTS) $(INSTALL_CONF_SCRIPTS)
+
 SCRIPTS = $(NONCONF_SCRIPTS) $(CONF_SCRIPTS)

 CONF_FILES = \
@@ -112,17 +118,17 @@
 	cp `vpath_find Mmake.vars Mmake.rules` $(INSTALL_LIBDIR)/mmake

 .PHONY: install_scripts
-install_scripts: $(SCRIPTS) install_dirs
+install_scripts: $(INSTALL_SCRIPTS) install_dirs
 	# we move the installed `mmake' script before (re)installing it
 	# to avoid overwriting the script while it is running
 	# (just removing it doesn't work very well on win32, which will
 	# deny you permission to write the file while mmake is running).
 	-mv $(INSTALL_BINDIR)/mmake $(INSTALL_BINDIR)/mmake.old
-	cp $(SCRIPTS) $(INSTALL_BINDIR)
-	-for file in $(SCRIPTS); do \
+	cp $(INSTALL_SCRIPTS) $(INSTALL_BINDIR)
+	-for file in $(INSTALL_SCRIPTS); do \
 		chmod u+w $(INSTALL_BINDIR)/$$file ;\
 	done
-	cp *.in *.sh-subr $(SCRIPTS) $(INSTALL_RECONF_DIR)/scripts
+	cp *.in *.sh-subr $(INSTALL_SCRIPTS) $(INSTALL_RECONF_DIR)/scripts
 	-rm -f $(INSTALL_BINDIR)/mmake.old

 .PHONY: install_config
@@ -149,7 +155,7 @@
 .PHONY: uninstall
 uninstall:
 	-rm -r $(INSTALL_LIBDIR)/mmake
-	-cd $(INSTALL_BINDIR) && rm $(SCRIPTS)
+	-cd $(INSTALL_BINDIR) && rm $(INSTALL_SCRIPTS)
 	-cd $(INSTALL_LIBDIR)/mdb && rm $(DEBUGGER_SCRIPTS)
 	# The reason why we don't uninstall $(EMACS_SCRIPTS) specifically here
 	# is that the uninstall rule in the top-level Mmakefile does an "rm -r"
Index: scripts/ml.in
===================================================================
RCS file: /home/mercury1/repository/mercury/scripts/ml.in,v
retrieving revision 1.114
diff -u -r1.114 ml.in
--- scripts/ml.in	20 Sep 2005 04:26:49 -0000	1.114
+++ scripts/ml.in	30 Sep 2005 10:35:01 -0000
@@ -58,7 +58,11 @@
 SHARED_LIBS=${MERCURY_SHARED_LIBS="@SHARED_LIBS_SH@"}

 # Set the MACOSX_DEPLOYMENT_TARGET environment variable if needed.
- at SET_MACOSX_DEPLOYMENT_TARGET@
+case "@SET_MACOSX_DEPLOYMENT_TARGET@" in
+	1)	MACOSX_DEPLOYMENT_TARGET=10.3
+		export MACOSX_DEPLOYMENT_TARGET
+		;;
+esac

 # When compiling in the hlc.gc grade using the Microsoft Visual C
 # compiler, the default maximum stack size of 4Mb is too low for a
Index: scripts/mmake.in
===================================================================
RCS file: /home/mercury1/repository/mercury/scripts/mmake.in,v
retrieving revision 1.46
diff -u -r1.46 mmake.in
--- scripts/mmake.in	11 Aug 2005 10:40:48 -0000	1.46
+++ scripts/mmake.in	29 Sep 2005 02:36:04 -0000
@@ -81,7 +81,11 @@
 TMPDIR=${TMPDIR=/tmp}

 # Set the MACOSX_DEPLOYMENT_TARGET environment variable if needed.
- at SET_MACOSX_DEPLOYMENT_TARGET@
+case "@SET_MACOSX_DEPLOYMENT_TARGET@" in
+	1)	MACOSX_DEPLOYMENT_TARGET=10.3
+		export MACOSX_DEPLOYMENT_TARGET
+		;;
+esac

 MMAKE=$0
 include_makefile=
Index: scripts/mmc.in
===================================================================
RCS file: /home/mercury1/repository/mercury/scripts/mmc.in,v
retrieving revision 1.37
diff -u -r1.37 mmc.in
--- scripts/mmc.in	23 Dec 2004 04:19:36 -0000	1.37
+++ scripts/mmc.in	1 Oct 2005 06:45:55 -0000
@@ -6,9 +6,15 @@
 # Public License - see the file COPYING in the Mercury distribution.
 #---------------------------------------------------------------------------#
 #
-# MMC - Melbourne Mercury Compiler.
+# mmc - a wrapper for the Melbourne Mercury Compiler.
 #
-# Use `mmc -h' for help.
+# NOTE:  Any changes to this file will probably also require changes to
+#        ../util/mmc.c.in.
+# 	 This is a shell script version of ../util/mmc.c.in that does not
+# 	 support the standalone mmc mode (see ../README.Standalone).  This
+# 	 version of mmc is not installed, but is used in the compiler build
+#        process and may be useful for developers since it is easier to
+# 	 change than the C version.
 #
 # Environment variables: MERCURY_STDLIB_DIR, MERCURY_CONFIG_DIR,
 # MERCURY_COMPILER, MERCURY_C_COMPILER, MERCURY_DEFAULT_GRADE,
@@ -19,7 +25,11 @@
 export MERCURY_COMPILER MERCURY_CONFIG_DIR

 # Set the MACOSX_DEPLOYMENT_TARGET environment variable if needed.
- at SET_MACOSX_DEPLOYMENT_TARGET@
+case "@SET_MACOSX_DEPLOYMENT_TARGET@" in
+	1)	MACOSX_DEPLOYMENT_TARGET=10.3
+		export MACOSX_DEPLOYMENT_TARGET
+		;;
+esac

 case $# in
 	0) exec $MERCURY_COMPILER ;;
Index: util/Mmakefile
===================================================================
RCS file: /home/mercury1/repository/mercury/util/Mmakefile,v
retrieving revision 1.17
diff -u -r1.17 Mmakefile
--- util/Mmakefile	14 Jan 2005 05:53:44 -0000	1.17
+++ util/Mmakefile	1 Oct 2005 04:17:03 -0000
@@ -4,7 +4,7 @@
 # Public License - see the file COPYING in the Mercury distribution.
 #-----------------------------------------------------------------------------#

-# Mmake - Mmake file for the Mercury utilities directory
+# Mmakefile - Mmake file for the Mercury utilities directory

 MAIN_TARGET=all

@@ -36,7 +36,11 @@

 #-----------------------------------------------------------------------------#

-all:	$(PROGS) $(TAGS_FILE_EXISTS)
+all:	$(PROGS) $(TAGS_FILE_EXISTS) mmc$(EXT_FOR_EXE)
+
+mmc$(EXT_FOR_EXE): mmc.c
+	$(MGNUC) --no-ansi $(GRADEFLAGS) $(ALL_MGNUCFLAGS) \
+		-o mmc$(EXT_FOR_EXE) mmc.c path_util.c

 .c:
 	$(MGNUC) $(GRADEFLAGS) $(ALL_MGNUCFLAGS) -o $@ $< $(GETOPT_SRC)
@@ -52,17 +56,18 @@
 #-----------------------------------------------------------------------------#

 .PHONY: install
-install: $(PROGS)
+install: $(PROGS) mmc$(EXT_FOR_EXE)
 	[ -d $(INSTALL_BINDIR) ] || mkdir -p $(INSTALL_BINDIR)
 	cp `vpath_find $(PROGFILENAMES)` $(INSTALL_BINDIR)
+	cp `vpath_find mmc$(EXT_FOR_EXE)` $(INSTALL_BINDIR)

 .PHONY: uninstall
 uninstall:
-	-cd $(INSTALL_BINDIR) && rm $(PROGS)
+	-cd $(INSTALL_BINDIR) && rm $(PROGS) && rm mmc$(EXT_FOR_EXE)

 #-----------------------------------------------------------------------------#

 realclean_local:
-	-rm -f $(PROGFILENAMES)
+	-rm -f $(PROGFILENAMES) mmc$(EXT_FOR_EXE)

 #-----------------------------------------------------------------------------#
Index: util/mmc.c.in
===================================================================
RCS file: util/mmc.c.in
diff -N util/mmc.c.in
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ util/mmc.c.in	1 Oct 2005 08:34:22 -0000
@@ -0,0 +1,149 @@
+/*
+** vim: ts=4 sw=4 expandtab
+*/
+/*
+** Copyright (C) 2005 The University of Melbourne.
+** This file may only be copied under the terms of the GNU Library General
+** Public License - see the file COPYING.LIB in the Mercury distribution.
+*/
+
+/*
+** Author: Ian MacLarty.
+**
+** mmc - a wrapper for the Melbourne Mercury Compiler.
+**
+** NOTE:  Any changes to this file may also require changes to
+**        ../scripts/mmc.in.
+**        This is a C version of ../scripts/mmc.in.  This version also
+**        supports the standalone mode (see ../README.Standalone), which
+**        ../scripts/mmc.in does not.
+**        This version of mmc is the one which is installed.
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "path_util.h"
+
+#define MERCURY_COMPILER_RELATIVE_PATH \
+    "lib/mercury/bin/@FULLARCH@/mercury_compile"
+
+#define MERCURY_CONFIG_DIR_RELATIVE_PATH \
+    "lib/mercury"
+
+#define MERCURY_LINK_CMD_FLAG \
+    "--link-executable-command"
+
+#define MERCURY_LINK_EXEC_CMD_FLAG \
+    "--link-executable-command"
+
+#define MERCURY_LINK_SHARED_LIB_FLAG \
+    "--link-shared-lib-command"
+
+int
+main(int argc, char **argv)
+{
+    char    *invoke_mercury_compiler_command;
+    char    *env_mercury_compiler;
+    char    *env_mercury_config_dir;
+    char    *env_mercury_stdlib_dir;
+    char    *mercury_compiler_absolute;
+    char    *mercury_config_dir_absolute;
+    char    *extra_mercury_compiler_flags;
+    char    *tmp_ptr;
+    int     argnum;
+
+#if @MMC_STANDALONE@
+    {
+        char    *mercury_bin;
+        char    *mercury_home;
+        char    *mercury_c_compiler_absolute;
+
+        mercury_bin = find_exec_dir(argv[0]);
+
+        if (mercury_bin == NULL) {
+            fprintf(stderr, "mmc: Cannot find path to mmc.\n"
+                "Make sure the directory containing mmc is in your PATH.\n");
+            exit(1);
+        }
+
+        tmp_ptr = mercury_bin;
+        mercury_bin = quote_directories_with_spaces(mercury_bin);
+        free(tmp_ptr);
+
+        set_env_var("PATH", mercury_bin, ENVVAR_PREFIX);
+
+        mercury_home = new_formatted_string("%s%s..", mercury_bin,
+            DIR_SEP_STR);
+
+        mercury_compiler_absolute = new_formatted_string("%s%s%s",
+            mercury_home, DIR_SEP_STR, MERCURY_COMPILER_RELATIVE_PATH);
+        convert_dir_separators(mercury_compiler_absolute);
+
+        mercury_config_dir_absolute = new_formatted_string("%s/%s",
+            mercury_home, MERCURY_CONFIG_DIR_RELATIVE_PATH);
+        convert_dir_separators(mercury_config_dir_absolute);
+        set_env_var("MERCURY_STDLIB_DIR", mercury_config_dir_absolute,
+            ENVVAR_CREATE_IF_UNSET);
+
+        mercury_c_compiler_absolute = new_formatted_string("%s/%s",
+            mercury_home, "@C_COMPILER_RELATIVE_PATH@");
+        convert_dir_separators(mercury_c_compiler_absolute);
+        set_env_var("MERCURY_C_COMPILER", mercury_c_compiler_absolute,
+            ENVVAR_CREATE_IF_UNSET);
+
+        extra_mercury_compiler_flags = new_formatted_string(
+            "%s %s/%s %s %s/%s",
+            MERCURY_LINK_EXEC_CMD_FLAG,
+            mercury_home,
+            "@LINK_EXEC_CMD_RELATIVE_PATH@",
+            MERCURY_LINK_SHARED_LIB_FLAG,
+            mercury_home,
+            "@LINK_SHARED_LIB_CMD_RELATIVE_PATH@");
+        convert_dir_separators(extra_mercury_compiler_flags);
+    }
+#else /* @MMC_STANDALONE@ */
+    mercury_compiler_absolute =
+        copy_string("@LIBDIR@/bin/@FULLARCH@/mercury_compile");
+    mercury_config_dir_absolute =
+        copy_string("@CONFIG_LIBDIR@");
+    extra_mercury_compiler_flags =
+        copy_string("");
+    convert_dir_separators(mercury_compiler_absolute);
+    convert_dir_separators(mercury_config_dir_absolute);
+#endif /* @MMC_STANDALONE@ */
+
+    env_mercury_compiler = getenv("MERCURY_COMPILER");
+    env_mercury_config_dir = getenv("MERCURY_CONFIG_DIR");
+    env_mercury_stdlib_dir = getenv("MERCURY_STDLIB_DIR");
+
+    if (env_mercury_compiler == NULL) {
+        set_env_var("MERCURY_COMPILER", mercury_compiler_absolute,
+            ENVVAR_CREATE_IF_UNSET);
+    } else {
+        mercury_compiler_absolute = env_mercury_compiler;
+    }
+    if (env_mercury_config_dir == NULL) {
+        if (env_mercury_stdlib_dir == NULL) {
+            set_env_var("MERCURY_CONFIG_DIR", mercury_config_dir_absolute,
+                ENVVAR_CREATE_IF_UNSET);
+        } else {
+            set_env_var("MERCURY_CONFIG_DIR", env_mercury_stdlib_dir,
+                ENVVAR_CREATE_IF_UNSET);
+        }
+    }
+
+#if @SET_MACOSX_DEPLOYMENT_TARGET@
+    set_env_var("MACOSX_DEPLOYMENT_TARGET", "10.3", ENVVAR_CREATE_IF_UNSET);
+#endif
+
+    invoke_mercury_compiler_command = new_formatted_string("%s %s",
+        mercury_compiler_absolute, extra_mercury_compiler_flags);
+    for (argnum = 1; argnum < argc; argnum++) {
+        tmp_ptr = invoke_mercury_compiler_command;
+        invoke_mercury_compiler_command = new_formatted_string("%s %s",
+            invoke_mercury_compiler_command, argv[argnum]);
+        free(tmp_ptr);
+    }
+    return system(invoke_mercury_compiler_command);
+}
Index: util/path_util.c
===================================================================
RCS file: util/path_util.c
diff -N util/path_util.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ util/path_util.c	1 Oct 2005 08:43:52 -0000
@@ -0,0 +1,394 @@
+/*
+** vim: ts=4 sw=4 expandtab
+*/
+
+/*
+** Copyright (C) 2005 The University of Melbourne.
+** This file may only be copied under the terms of the GNU Library General
+** Public License - see the file COPYING.LIB in the Mercury distribution.
+*/
+
+/*
+** Author: Ian MacLarty.
+**
+** This file implements some utility functions for manipulating paths and
+** working out were an executable is being executed from.
+*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include "mercury_conf.h"
+
+#include "path_util.h"
+
+#ifdef MR_HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#define MAX_PATH_SIZE   8128
+
+static  void    split_path_into_dir_and_file(char *path, char **dir,
+                    char **filename);
+static  int     file_exists_and_is_executable(char* path);
+static  char    *search_path(char *path, const char *path_separator,
+                    char *filename);
+static  char*   copy_substring(char* str, int start, int end);
+static  void    delete_quotes(char* str);
+
+char*
+find_exec_dir(char* cmd)
+{
+    char    *filename;
+    char    *cmd_dir;
+    char    *found_dir;
+    char    working_dir[MAX_PATH_SIZE];
+    char    *absolute_path;
+    char    *path_env;
+
+    split_path_into_dir_and_file(cmd, &cmd_dir, &filename);
+
+    /*
+    ** Check if the first argument is an absolute path.
+    */
+    if (IS_ABS_PATH(cmd_dir)) {
+        free(filename);
+        return cmd_dir;
+    }
+
+    /*
+    ** See if the first argument is a path relative to the current working
+    ** directory.
+    */
+    if (getcwd(working_dir, MAX_PATH_SIZE)) {
+        absolute_path = new_formatted_string("%s%s%s", working_dir,
+            DIR_SEP_STR, cmd);
+        if (file_exists_and_is_executable(absolute_path)) {
+            free(filename);
+            split_path_into_dir_and_file(absolute_path, &found_dir,
+                &filename);
+            free(filename);
+            return found_dir;
+        }
+    }
+
+    /*
+    ** Search the PATH.
+    */
+    free(cmd_dir);
+    path_env = getenv("PATH");
+
+    if (path_env != NULL) {
+        found_dir = search_path(path_env, PATH_SEP_STR, filename);
+        free(filename);
+        return found_dir;
+    }
+    free(filename);
+    return NULL;
+}
+
+void
+convert_dir_separators(char* dir)
+{
+#ifdef __WIN32
+    char    *chr_ptr;
+
+    chr_ptr = strchr(dir, '/');
+    while (chr_ptr != NULL) {
+        *chr_ptr = '\\';
+        chr_ptr = strchr(dir, '/');
+    }
+#endif /* __WIN32 */
+}
+
+void
+set_env_var(const char *var, char* value, set_env_var_behaviour how)
+{
+    const char*   current_value;
+    char*   set_string;
+
+    current_value = getenv(var);
+    if (current_value == NULL) {
+        current_value = "";
+    } else if (how == ENVVAR_CREATE_IF_UNSET) {
+        return;
+    }
+
+    switch (how) {
+        case ENVVAR_OVERWRITE:
+            set_string = (char*)malloc(strlen(var) + 1 + strlen(value) + 1);
+            sprintf(set_string, "%s=%s", var, value);
+            break;
+        case ENVVAR_PREFIX:
+            set_string = (char*)malloc(strlen(var) + 1 + strlen(value)
+                + strlen(PATH_SEP_STR) + strlen(current_value) + 1);
+            sprintf(set_string, "%s=%s%s%s", var, value, PATH_SEP_STR,
+                current_value);
+            break;
+        case ENVVAR_APPEND:
+            set_string = (char*)malloc(strlen(var) + 1 + strlen(current_value)
+                + strlen(PATH_SEP_STR) + strlen(value) + 1);
+            sprintf(set_string, "%s=%s%s%s", var, current_value, PATH_SEP_STR,
+                value);
+            break;
+        case ENVVAR_CREATE_IF_UNSET:
+            set_string = (char*)malloc(strlen(var) + 1 + strlen(value) + 1);
+            sprintf(set_string, "%s=%s", var, value);
+            break;
+        default:
+            fprintf(stderr, "unknown set_env_var_behaviour value");
+            exit(1);
+    }
+
+    if (putenv(set_string) != 0) {
+        fprintf(stderr, "unable to set environment %s=%s.\n", var, value);
+        exit(1);
+    }
+    /*
+    ** We don't free set_string, since any changes to the contents of
+    ** it will alter the environment (according to the Linux manpage).
+    */
+}
+
+char*
+quote_directories_with_spaces(char *path)
+{
+    int     length;
+    char    *new_path;
+    int     pos;
+    int     found_space;
+    int     dir_start;
+    char    *one_dir;
+    char    *tmp_ptr;
+
+    length = strlen(path);
+    new_path = copy_string("");
+    found_space = 0;
+    dir_start = 0;
+
+    for (pos = 0; pos < length; pos++) {
+        if (path[pos] == ' ') {
+            found_space = 1;
+        }
+        if (IS_DIR_SEP(path[pos])) {
+            one_dir = copy_substring(path, dir_start, pos - 1);
+            tmp_ptr = new_path;
+            if (found_space) {
+                new_path = new_formatted_string("%s\"%s\"%s", tmp_ptr,
+                    one_dir, DIR_SEP_STR);
+                found_space = 0;
+            } else {
+                new_path = new_formatted_string("%s%s%s", tmp_ptr,
+                    one_dir, DIR_SEP_STR);
+            }
+            free(tmp_ptr);
+            free(one_dir);
+            dir_start = pos + 1;
+        }
+    }
+    one_dir = copy_substring(path, dir_start, pos - 1);
+    tmp_ptr = new_path;
+    if (found_space) {
+        new_path = new_formatted_string("%s\"%s\"", tmp_ptr, one_dir);
+        found_space = 0;
+    } else {
+        new_path = new_formatted_string("%s%s", tmp_ptr, one_dir);
+    }
+    free(tmp_ptr);
+    free(one_dir);
+
+    return new_path;
+}
+
+/*
+** Split the given path into directory and filename components.
+** *dir and *filename are set to newly alloctated strings containing the
+** directory and filename parts of the path respectively.
+*/
+
+static void
+split_path_into_dir_and_file(char *path, char **dir, char **filename)
+{
+    char    *last_dir_sep;
+
+    last_dir_sep = strrchr(path, DIR_SEP_CHR);
+    if (last_dir_sep != NULL) {
+        *filename = copy_string((char*)(last_dir_sep + 1));
+        *dir = copy_substring(path, 0, last_dir_sep - path - 1);
+    } else {
+        *filename = copy_string(path);
+        *dir = copy_string("");
+    }
+}
+
+/*
+** Return 1 if the given file exists and is executable.  Otherwise
+** return 0.  On Windows this function will append ".exe" to the filename
+** before checking if it exists if there is no ".exe" there already
+** (the string pointed to by path remains unchanged).
+*/
+
+static int
+file_exists_and_is_executable(char* path)
+{
+    FILE    *fp;
+    char    *exe_pos;
+    char    path_with_exe[MAX_PATH_SIZE];
+
+    if (strlen(path) >= MAX_PATH_SIZE) {
+        return 0;
+    }
+#if defined(__WIN32) || defined(__CYGWIN__)
+    exe_pos = strstr(path, ".exe");
+    if (exe_pos == NULL || strcmp(exe_pos, ".exe") != 0) {
+        if (snprintf(path_with_exe, MAX_PATH_SIZE - 1, "%s%s", path, ".exe")
+            >= MAX_PATH_SIZE)
+        {
+            return 0;
+        }
+    } else {
+        strncpy(path_with_exe, path, MAX_PATH_SIZE - 1);
+    }
+#else /* defined(__WIN32) || defined(__CYGWIN__) */
+    strncpy(path_with_exe, path, MAX_PATH_SIZE - 1);
+#endif /* defined(__WIN32) || defined(__CYGWIN__) */
+
+#ifdef MR_HAVE_UNISTD_H
+    if (access(path_with_exe, X_OK) == 0) {
+        return 1;
+    }
+#else
+    fp = fopen(path_with_exe, "r");
+    if (fp != NULL) {
+        fclose(fp);
+        return 1;
+    }
+#endif /* MR_HAVE_UNISTD_H */
+
+    return 0;
+}
+
+/*
+** Search the given path for the executable file given by filename,
+** using path_separator as the path separator (`:' on unix, `;' on Windows).
+** If the file is found then return the path to the directory containing the
+** file, otherwise return NULL.
+** The returned string will be in a newly allocated block of memory.
+*/
+
+static char*
+search_path(char *path, const char *path_separator, char *filename)
+{
+    char    *dir;
+    char    *dirs;
+    char    *found_dir;
+    char    *absolute_path;
+
+    dirs = copy_string(path);
+#ifdef _WIN32
+    /*
+    ** On Windows, directory names that need to be quoted (for example
+    ** directory names with spaces) may have the quote characters in the PATH
+    ** entry.
+    */
+    delete_quotes(dirs);
+#endif
+
+    dir = (char*)strtok(dirs, path_separator);
+    do {
+        absolute_path = new_formatted_string("%s/%s", dir, filename);
+        convert_dir_separators(absolute_path);
+        if (file_exists_and_is_executable(absolute_path)) {
+            found_dir = copy_string(dir);
+            free(dirs);
+            return found_dir;
+        }
+        dir = (char*)strtok(NULL, path_separator);
+    } while (dir != NULL);
+
+    free(dirs);
+    return NULL;
+}
+
+/*
+** The following code is taken from the sprintf Linux man page
+** (slightly modified).
+*/
+char *
+new_formatted_string(const char *fmt, ...) {
+    /* Guess we need no more than 100 bytes. */
+    int     n;
+    int     size = 100;
+    char    *p;
+    va_list ap;
+
+    if ((p = malloc (size)) == NULL) {
+        return NULL;
+    }
+    while (1) {
+        /* Try to print in the allocated space. */
+        va_start(ap, fmt);
+        n = vsnprintf (p, size, fmt, ap);
+        va_end(ap);
+        /* If that worked, return the string. */
+        if (n > -1 && n < size) {
+            return p;
+        }
+        /* Else try again with more space. */
+        if (n > -1) {   /* glibc 2.1 */
+            size = n+1; /* precisely what is needed */
+        } else {        /* glibc 2.0 */
+            size *= 2;  /* twice the old size */
+        }
+        if ((p = realloc (p, size)) == NULL) {
+            return NULL;
+        }
+    }
+}
+
+char*
+copy_string(const char* str)
+{
+    char*   new_str;
+
+    new_str = malloc(strlen(str) + 1);
+    strcpy(new_str, str);
+    return new_str;
+}
+
+static  char*
+copy_substring(char* str, int start, int end)
+{
+    char*   new_str;
+
+    if (end < start) {
+        new_str = copy_string("");
+        return new_str;
+    } else {
+        new_str = malloc(end - start + 2);
+        strncpy(new_str, &str[start], end - start + 1);
+        new_str[end - start + 1] = '\0';
+        return new_str;
+    }
+}
+
+static  void
+delete_quotes(char* str)
+{
+    int     read_pos;
+    int     write_pos;
+    int     len;
+
+    len = strlen(str);
+    write_pos = 0;
+    for (read_pos = 0; read_pos < len; read_pos++) {
+        if (str[read_pos] != '\"') {
+            str[write_pos] = str[read_pos];
+            write_pos++;
+        }
+    }
+    str[write_pos] = '\0';
+}
Index: util/path_util.h
===================================================================
RCS file: util/path_util.h
diff -N util/path_util.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ util/path_util.h	1 Oct 2005 08:46:19 -0000
@@ -0,0 +1,100 @@
+/*
+** vim: ts=4 sw=4 expandtab
+*/
+/*
+** Copyright (C) 2005 The University of Melbourne.
+** This file may only be copied under the terms of the GNU Library General
+** Public License - see the file COPYING.LIB in the Mercury distribution.
+*/
+
+/*
+** Author: Ian MacLarty.
+**
+** This file exports some utility functions for manipulating paths and
+** working out were an executable is being executed from.
+*/
+
+/*
+** Find the location of the executable that was invoked with the
+** command cmd.
+** This function first checks if cmd contains an absolute path,
+** then if it contains a path relative to the current working directory,
+** and finally it searches the PATH for an executable file with the same
+** name as the file given in cmd.
+** On success a newly allocated string containing the directory is returned,
+** otherwise NULL is returned.
+*/
+
+extern  char*   find_exec_dir(char* cmd);
+
+/*
+** This function converts any `/' characters in the given string to `\'
+** characters if __WIN32 is defined.  If __WIN32 is undefined then
+** the string is left unchanged.
+*/
+
+extern  void    convert_dir_separators(char* dir);
+
+/*
+** Change the given environment variable.
+** The how argument says how the environment variable should be changed.
+** For ENVVAR_PREFIX and ENVVAR_APPEND, the default path seperator will be
+** used to seperate the old value from the prefixed or appended value
+** (on unix the path seperator is `:', while on Windows it is `;').
+** If memory could not be allocated for the environment variable then
+** this function displays an error and aborts the program.
+*/
+
+typedef enum {
+    ENVVAR_OVERWRITE,
+    ENVVAR_PREFIX,
+    ENVVAR_APPEND,
+    ENVVAR_CREATE_IF_UNSET
+} set_env_var_behaviour;
+
+extern  void    set_env_var(const char *var, char* value, set_env_var_behaviour how);
+
+/*
+** Put quotes around directories that contain spaces in the given path
+** and return a newly allocated string containing the result.
+*/
+
+extern  char*   quote_directories_with_spaces(char *path);
+
+/*
+** new_formatted_string creates a new string based on the format string fmt
+** and the rest of its arguments and returns the result in a newly allocated
+** string.
+*/
+
+extern  char    *new_formatted_string(const char *fmt, ...);
+
+/*
+** Return a reference to a copy of the given string.
+*/
+
+extern  char*   copy_string(const char* str);
+
+#ifdef _WIN32
+
+#define IS_DIR_SEP(c) (c == '\\')
+#define DIR_SEP_STR "\\"
+#define DIR_SEP_CHR '\\'
+#define PATH_SEP_STR ";"
+#define WIN32_DRIVE_LETTERS \
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+#define IS_ABS_PATH(p) \
+    ((strlen(p) >= 3 && strchr(WIN32_DRIVE_LETTERS, p[0]) &&                  \
+        p[1] == ':' && p[2] == '\\')                                          \
+        || (strlen(p) >= 2 && p[0] == '\\' && p[1] == '\\'))
+
+#else /* ifdef _WIN32 */
+
+#define IS_DIR_SEP(c) (c == '/')
+#define DIR_SEP_STR "/"
+#define DIR_SEP_CHR '/'
+#define PATH_SEP_STR ":"
+#define IS_ABS_PATH(p) (p[0] == '/')
+
+#endif /* ifdef _WIN32 */
+

--------------------------------------------------------------------------
mercury-reviews mailing list
post:  mercury-reviews at cs.mu.oz.au
administrative address: owner-mercury-reviews at cs.mu.oz.au
unsubscribe: Address: mercury-reviews-request at cs.mu.oz.au Message: unsubscribe
subscribe:   Address: mercury-reviews-request at cs.mu.oz.au Message: subscribe
--------------------------------------------------------------------------



More information about the reviews mailing list