From cc4dbcb83be19fd55e6dae28ea37a714109bec26 Mon Sep 17 00:00:00 2001
From: Jozef Lawrynowicz <jozef.l@mittosystems.com>
Date: Mon, 29 Oct 2018 13:52:39 +0000
Subject: [PATCH 3/3] MSP430 - Devices 3 - Add functionality to read device
 data from environment variable

2018-10-29  Jozef Lawrynowicz  <jozef.l@mittosystems.com>

gcc/config/msp430/

	* driver-msp430.c (msp430_select_hwmult_lib): Fix formatting.
	(msp430_get_linker_devices_include_path): New.
	* msp430-devices.c (msp430_dirname): New.
	(canonicalize_path_dirsep): New.
	(process_collect_gcc_into_devices_dir): New.
	(msp430_check_env_var_for_devices): New.
	(parse_devices_csv): Call msp430_check_env_var_for_devices after trying
	other methods.
	* msp430-devices.h (msp430_check_env_var_for_devices): New.
	(msp430_dirname): New.
	* msp430.c (msp430_register_pre_includes): New.
	* msp430.h (EXTRA_SPEC_FUNCTIONS): Add
	msp430_get_linker_devices_include_path.
	(LINK_SPEC): Likewise.
	Define TARGET_EXTRA_PRE_INCLUDES to msp430_register_pre_includes.

gcc/doc/

	* invoke.texi: Document that devices.csv is searched for using an
	environment variable and in a subdirectory of the toolchain
	installation.

gcc/testsuite/gcc.target/msp430/

	* devices/csv_using_env_var.c: New.
	* devices/csv_using_installed.c: New.
	* devices/csv_using_option.c: New.
	* msp430.exp (msp430_device_permutations_runtest): Prepare for
	csv_using_* tests as appropriate.
	(get_installed_device_data_path): New.
	(msp430_hide_installed_devices_data): New.
	(msp430_restore_installed_devices_data): New.
	(msp430_test_installed_device_data): New.
---
 gcc/config/msp430/driver-msp430.c                  | 13 +++-
 gcc/config/msp430/msp430-devices.c                 | 88 +++++++++++++++++++++-
 gcc/config/msp430/msp430-devices.h                 |  2 +
 gcc/config/msp430/msp430.c                         | 16 ++++
 gcc/config/msp430/msp430.h                         | 12 ++-
 gcc/doc/invoke.texi                                | 28 ++++++-
 .../gcc.target/msp430/devices/csv_using_env_var.c  | 10 +++
 .../msp430/devices/csv_using_installed.c           | 11 +++
 .../gcc.target/msp430/devices/csv_using_option.c   | 12 +++
 gcc/testsuite/gcc.target/msp430/msp430.exp         | 70 ++++++++++++++++-
 10 files changed, 255 insertions(+), 7 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/csv_using_env_var.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/csv_using_installed.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/csv_using_option.c

diff --git a/gcc/config/msp430/driver-msp430.c b/gcc/config/msp430/driver-msp430.c
index 4242274..3facdc2 100644
--- a/gcc/config/msp430/driver-msp430.c
+++ b/gcc/config/msp430/driver-msp430.c
@@ -52,7 +52,8 @@ msp430_select_cpu (int argc, const char ** argv)
 /* Implement spec function `msp430_hwmult_lib´.  */
 
 const char *
-msp430_select_hwmult_lib (int argc ATTRIBUTE_UNUSED, const char ** argv ATTRIBUTE_UNUSED)
+msp430_select_hwmult_lib (int argc ATTRIBUTE_UNUSED,
+			  const char ** argv ATTRIBUTE_UNUSED)
 {
   int i;
 
@@ -116,3 +117,13 @@ msp430_select_hwmult_lib (int argc ATTRIBUTE_UNUSED, const char ** argv ATTRIBUT
   
   return "-lmul_none";
 }
+
+const char *
+msp430_get_linker_devices_include_path (int argc ATTRIBUTE_UNUSED,
+					const char **argv ATTRIBUTE_UNUSED)
+{
+  char *devices_csv_path;
+  if (msp430_check_env_var_for_devices (&devices_csv_path))
+    return NULL;
+  return concat ("-L", msp430_dirname (devices_csv_path), NULL);
+}
diff --git a/gcc/config/msp430/msp430-devices.c b/gcc/config/msp430/msp430-devices.c
index 2f44861..c37f8dc 100644
--- a/gcc/config/msp430/msp430-devices.c
+++ b/gcc/config/msp430/msp430-devices.c
@@ -23,6 +23,88 @@ char * derived_devices_csv_loc = NULL;
 /* Set to true by msp430_extract_mcu_data if called from the driver.  */
 bool surpress_devices_warn = false;
 
+/* This modifies the string pointed to by path.  */
+char *
+msp430_dirname (char *path)
+{
+  int last_elem = strlen (path) - 1;
+  int i = last_elem - (IS_DIR_SEPARATOR (path[last_elem]) ? 1 : 0);
+  for (; i >= 0; i--)
+    {
+      if (IS_DIR_SEPARATOR (path[i]))
+	{
+	  path[i] = '\0';
+	  return path;
+	}
+    }
+  return path;
+}
+
+/* This is to canonicalize the path.  On Windows we can have a mix of forward
+   and backslashes which leads to issues following paths.  */
+static void
+canonicalize_path_dirsep (char **path)
+{
+  char *t_path = *path;
+  unsigned int i;
+  for (i = 0; i < strlen (t_path); i++)
+    if (IS_DIR_SEPARATOR (t_path[i]))
+      t_path[i] = DIR_SEPARATOR;
+}
+
+static void
+process_collect_gcc_into_devices_dir (char **devices_loc)
+{
+  const char rest_of_devices_path[] = "/msp430-elf/include/devices/";
+  char *t_devices_loc = *devices_loc;
+  /* Go up a directory to the toolchain root.  */
+  t_devices_loc = msp430_dirname (msp430_dirname (t_devices_loc));
+  t_devices_loc = concat (t_devices_loc, rest_of_devices_path, NULL);
+  *devices_loc = t_devices_loc;
+}
+
+int
+msp430_check_env_var_for_devices (char **local_devices_csv_loc)
+{
+  const int num_vars = 2;
+  const char dirsep[2] = { DIR_SEPARATOR, 0 };
+  /* Use the COLLECT_GCC environment variable to find toolchain root directory.
+   */
+  const char *env_vars[num_vars] = { "MSP430_GCC_INCLUDE_DIR", "COLLECT_GCC" };
+  FILE *devices_csv_file = NULL;
+  unsigned int i;
+
+  for (i = 0; i < num_vars; i++)
+    {
+      char *t_devices_loc;
+      char *val = getenv (env_vars[i]);
+      if (val == NULL)
+	continue;
+      t_devices_loc = ASTRDUP (val);
+
+      /* MSP430_GCC_INCLUDE_DIR.  */
+      if (i == 0)
+	{
+	  if (!IS_DIR_SEPARATOR (t_devices_loc[strlen (t_devices_loc) - 1]))
+	    t_devices_loc = concat (t_devices_loc, dirsep, NULL);
+	}
+      /* COLLECT_GCC.  */
+      else if (i == 1)
+	process_collect_gcc_into_devices_dir (&t_devices_loc);
+
+      t_devices_loc = concat (t_devices_loc, "devices.csv", NULL);
+      devices_csv_file = fopen (t_devices_loc,  "r");
+      if (devices_csv_file != NULL)
+	{
+	  fclose (devices_csv_file);
+	  *local_devices_csv_loc = t_devices_loc;
+	  canonicalize_path_dirsep (local_devices_csv_loc);
+	  return 0;
+	}
+    }
+  return 1;
+}
+
 const char *
 msp430_check_path_for_devices (int argc, const char **argv)
 {
@@ -38,6 +120,7 @@ msp430_check_path_for_devices (int argc, const char **argv)
   for (i = 0; i < argc; i++)
     {
       char *inc_path = ASTRDUP (argv[i]);
+      canonicalize_path_dirsep (&inc_path);
       if (!IS_DIR_SEPARATOR (inc_path[strlen (inc_path) - 1]))
 	inc_path = concat (inc_path, dirsep, NULL);
       local_devices_csv_loc = concat (inc_path, "devices.csv", NULL);
@@ -206,7 +289,10 @@ parse_devices_csv (const char * mcu_name)
   /* Otherwise check if the path to devices.csv was found another way.  */
   else if (derived_devices_csv_loc != NULL)
     return parse_devices_csv_1 (derived_devices_csv_loc, mcu_name);
-  return 1;
+  /* Otherwise we need to use environment variables to try and find it.  */
+  else
+    return msp430_check_env_var_for_devices (&derived_devices_csv_loc)
+	    || parse_devices_csv_1 (derived_devices_csv_loc, mcu_name);
 }
 
 /* This function only needs to be executed once, but it can be first called
diff --git a/gcc/config/msp430/msp430-devices.h b/gcc/config/msp430/msp430-devices.h
index 9500115..e92927a 100644
--- a/gcc/config/msp430/msp430-devices.h
+++ b/gcc/config/msp430/msp430-devices.h
@@ -9,3 +9,5 @@ struct t_msp430_mcu_data
 extern struct t_msp430_mcu_data extracted_mcu_data;
 
 void msp430_extract_mcu_data (const char * mcu_name, bool surpress_warn);
+int msp430_check_env_var_for_devices (char **local_devices_csv_loc);
+char * msp430_dirname (char *path);
diff --git a/gcc/config/msp430/msp430.c b/gcc/config/msp430/msp430.c
index a112b1b..b22d67c 100644
--- a/gcc/config/msp430/msp430.c
+++ b/gcc/config/msp430/msp430.c
@@ -46,6 +46,8 @@
 #include "builtins.h"
 #include "intl.h"
 #include "msp430-devices.h"
+#include "incpath.h"
+#include "prefix.h"
 
 /* This file should be included last.  */
 #include "target-def.h"
@@ -3252,6 +3254,20 @@ msp430_can_change_mode_class (machine_mode from, machine_mode to, reg_class_t)
     return false;
   return true;
 }
+
+void
+msp430_register_pre_includes (const char *sysroot ATTRIBUTE_UNUSED,
+			      const char *iprefix ATTRIBUTE_UNUSED,
+			      int stdinc ATTRIBUTE_UNUSED)
+{
+  char *include_dir;
+  if (msp430_check_env_var_for_devices (&include_dir))
+    return;
+  include_dir = msp430_dirname (include_dir);
+
+  include_dir = update_path (include_dir, "");
+  add_path (include_dir, INC_SYSTEM, false, false);
+}
 
 #undef  TARGET_HAVE_SPECULATION_SAFE_VALUE
 #define TARGET_HAVE_SPECULATION_SAFE_VALUE speculation_safe_value_not_needed
diff --git a/gcc/config/msp430/msp430.h b/gcc/config/msp430/msp430.h
index a8daf3f..ce94116 100644
--- a/gcc/config/msp430/msp430.h
+++ b/gcc/config/msp430/msp430.h
@@ -47,11 +47,14 @@ extern bool msp430x;
 extern const char * msp430_select_hwmult_lib (int, const char **);
 extern const char * msp430_select_cpu (int, const char **);
 extern const char * msp430_check_path_for_devices (int, const char **);
+extern const char * msp430_get_linker_devices_include_path (int, const char **);
 
 # define EXTRA_SPEC_FUNCTIONS				\
   { "msp430_hwmult_lib", msp430_select_hwmult_lib }, \
   { "msp430_select_cpu", msp430_select_cpu }, \
-  { "msp430_check_path_for_devices", msp430_check_path_for_devices },
+  { "msp430_check_path_for_devices", msp430_check_path_for_devices }, \
+  { "msp430_get_linker_devices_include_path", \
+    msp430_get_linker_devices_include_path },
 
 #undef  STARTFILE_SPEC
 #define STARTFILE_SPEC "%{pg:gcrt0.o%s}%{!pg:%{minrt:crt0-minrt.o%s}%{!minrt:crt0.o%s}} %{!minrt:crtbegin.o%s}"
@@ -74,7 +77,8 @@ extern const char * msp430_check_path_for_devices (int, const char **);
    are creating a relocatable binary (gc does not work) or debugging
    is enabled  (the GDB testsuite relies upon unused entities not being deleted).  */
 #define LINK_SPEC "%{mrelax:--relax} %{mlarge:%{!r:%{!g:--gc-sections}}} " \
-  "%{mcode-region=*:--code-region=%*} %{mdata-region=*:--data-region=%*}"
+  "%{mcode-region=*:--code-region=%*} %{mdata-region=*:--data-region=%*} " \
+  "%:msp430_get_linker_devices_include_path(%{L*:%*})"
 
 #define DRIVER_SELF_SPECS \
   " %{I*:%:msp430_check_path_for_devices(%{I*:%*})}"       \
@@ -424,5 +428,9 @@ typedef struct
 #undef  USE_SELECT_SECTION_FOR_FUNCTIONS
 #define USE_SELECT_SECTION_FOR_FUNCTIONS 1
 
+void msp430_register_pre_includes (const char *sysroot, const char *iprefix,
+    int stdinc);
+#define TARGET_EXTRA_PRE_INCLUDES msp430_register_pre_includes
+
 #define ASM_OUTPUT_ALIGNED_DECL_COMMON(FILE, DECL, NAME, SIZE, ALIGN)	\
   msp430_output_aligned_decl_common ((FILE), (DECL), (NAME), (SIZE), (ALIGN))
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 19f32bc..bc09205 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -22068,8 +22068,32 @@ The ISA and hardware multiply supported for the different MCUs is hard-coded
 into GCC.  However, an external @samp{devices.csv} file can be used to
 extend device support beyond those that have been hard-coded.
 
-GCC searches for the @samp{devices.csv} file on the include paths specified
-with @code{-I}.
+GCC searches for the @samp{devices.csv} file using the following methods in the
+given precedence order, where the first method takes precendence over the
+second which takes precedence over the third.
+
+@table @asis
+@item Include path specified with @code{-I}
+@samp{devices.csv} will be searched for in each of the directories specified by
+include paths.
+@item Path specified by the environment variable @samp{MSP430_GCC_INCLUDE_DIR}
+Define the value of the global environment variable
+@samp{MSP430_GCC_INCLUDE_DIR}
+to the full path to the directory containing devices.csv, and GCC will search
+this directory for devices.csv.  If devices.csv is found, this directory will
+also be registered as an include path, and linker library path.  Header files
+and linker scripts in this directory can therefore be used without manually
+specifying @code{-I} and @code{-L} on the command line.
+@item The @samp{msp430-elf/include/devices} directory
+Finally, GCC will examine @samp{msp430-elf/include/devices} from the
+toolchain root directory.  This directory does not exist in a default
+installation, but if the user has created it and copied @samp{devices.csv}
+there, then the MCU data will be read.  As above, this directory will
+also be registered as an include path, and linker library path.
+
+@end table
+If none of the above search methods find @samp{devices.csv}, then the
+hard-coded MCU data is used.
 
 @item -mwarn-mcu
 @itemx -mno-warn-mcu
diff --git a/gcc/testsuite/gcc.target/msp430/devices/csv_using_env_var.c b/gcc/testsuite/gcc.target/msp430/devices/csv_using_env_var.c
new file mode 100644
index 0000000..506ae74
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/csv_using_env_var.c
@@ -0,0 +1,10 @@
+/* { dg-do link } */
+/* { dg-msp-options "-mmcu=msp430_28" } */
+/* { dg-warning "supports 430X ISA but -mcpu option is set to 430" "" { target 430_selected } 0 } */
+/* { dg-warning "supports f5series hardware multiply" "" { target msp430_hwmul_not_f5 } 0 } */
+
+/* This tests that the environment variable MSP430_GCC_INCLUDE_DIR can be used
+   to specify the path to the directory containing devices.csv.
+   The variable is set in msp430.exp.  */
+
+#include "../devices-main.c"
diff --git a/gcc/testsuite/gcc.target/msp430/devices/csv_using_installed.c b/gcc/testsuite/gcc.target/msp430/devices/csv_using_installed.c
new file mode 100644
index 0000000..f5d7806
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/csv_using_installed.c
@@ -0,0 +1,11 @@
+/* { dg-do link } */
+/* { dg-msp-options "-mmcu=msp430_28" } */
+/* { dg-warning "supports 430X ISA but -mcpu option is set to 430" "" { target 430_selected } 0 } */
+/* { dg-warning "supports f5series hardware multiply" "" { target msp430_hwmul_not_f5 } 0 } */
+
+/* This tests that the devices.csv can be installed into the
+   "$TOOLCHAIN_ROOT/msp430-elf/include/devices/" and used to read device data.
+   msp430.exp will mark this test unsupported if the above directory does not
+   exist.  */
+
+#include "../devices-main.c"
diff --git a/gcc/testsuite/gcc.target/msp430/devices/csv_using_option.c b/gcc/testsuite/gcc.target/msp430/devices/csv_using_option.c
new file mode 100644
index 0000000..c8d7f6d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/csv_using_option.c
@@ -0,0 +1,12 @@
+/* { dg-do link } */
+/* { dg-msp-options "-mmcu=msp430_28" } */
+/* { dg-warning "supports 430X ISA but -mcpu option is set to 430" "" { target 430_selected } 0 } */
+/* { dg-warning "supports f5series hardware multiply" "" { target msp430_hwmul_not_f5 } 0 } */
+
+/* This tests that the -mdevices-csv-loc option can be used to specify the path
+   to devices.csv.
+   The option is passed in msp430.exp, rather than in the dg-msp-options
+   directive above, as the full path to devices.csv is not known at this
+   point.  */
+
+#include "../devices-main.c"
diff --git a/gcc/testsuite/gcc.target/msp430/msp430.exp b/gcc/testsuite/gcc.target/msp430/msp430.exp
index 2cdf436..fe57469 100644
--- a/gcc/testsuite/gcc.target/msp430/msp430.exp
+++ b/gcc/testsuite/gcc.target/msp430/msp430.exp
@@ -215,6 +215,29 @@ proc msp430_device_permutations_runtest { tests default_cflags } {
 	if { ![runtest_file_p $runtests $test_file] } {
 	    continue
 	}
+ 	# Don't run torture style tests on every method of locating devices.csv
+ 	# The device name passed to -mmcu in the csv_using_* tests do not exist
+ 	# in the hard-coded data, so to ensure the test fails if method can't
+ 	# find the device data.
+ 	if { [file tail $test_file] eq "csv_using_installed.c" } {
+ 	    msp430_test_installed_device_data $test_file $default_cflags
+ 	    continue
+ 	# The path passed to -mdevices-csv-loc is not used to infer an
+ 	# include dir to find msp430.h, so define TESTING_HARD_DATA to prevent
+ 	# msp430.h being included.
+ 	} elseif { [file tail $test_file] eq "csv_using_option.c" } {
+ 	    dg-runtest $test_file \
+ 		"-mdevices-csv-loc=[file dirname $test_file]/devices.csv" \
+ 		"-DTESTING_HARD_DATA $default_cflags"
+ 	    continue
+ 	} elseif { [file tail $test_file] eq "csv_using_env_var.c" } {
+ 	    setenv MSP430_GCC_INCLUDE_DIR [file dirname $test_file]
+ 	    verbose -log "MSP430_GCC_INCLUDE_DIR=[file dirname $test_file]"
+ 	    dg-runtest $test_file "" "$default_cflags"
+ 	    setenv MSP430_GCC_INCLUDE_DIR ""
+ 	    verbose -log "MSP430_GCC_INCLUDE_DIR=\"\""
+ 	    continue
+ 	}
 	foreach { mcu_flags } [msp430_get_supported_opts $test_file] {
 	    if { [string match "csv_*" [file tail $test_file]] } {
 		dg-runtest $test_file "$mcu_flags" "-I[file dirname $test_file] $default_cflags"
@@ -228,6 +251,48 @@ proc msp430_device_permutations_runtest { tests default_cflags } {
     }
 }
 
+# Return $TOOLCHAIN_ROOT/msp430-elf/include/devices/
+proc get_installed_device_data_path { } {
+    set compiler [lindex [regexp -all -inline {\S+} \
+	[board_info [target_info name] compiler]] 0]
+    # $compiler is actually a file, but normalize will still get us the desired
+    # result.
+    return [file normalize \
+	"$compiler/../../msp430-elf/include/devices/devices.csv"]
+}
+
+# If the devices.csv is installed in
+# $TOOLCHAIN_ROOT/msp430-elf/include/devices/, rename it so it doesn't
+# interfere with the hard-coded device data tests.
+proc msp430_hide_installed_devices_data { } {
+    set devices_path [get_installed_device_data_path]
+    if { [file exists $devices_path] } {
+	file rename $devices_path "$devices_path.bak"
+    }
+}
+
+# Restore devices.csv if renamed by msp430_hide_installed_devices_data.
+proc msp430_restore_installed_devices_data { } {
+    set devices_path [get_installed_device_data_path]
+    if { [file exists "$devices_path.bak"] } {
+	file rename "$devices_path.bak" $devices_path 
+    }
+}
+
+proc msp430_test_installed_device_data { name default_cflags } {
+    global subdir
+    msp430_restore_installed_devices_data 
+    set devices_path [get_installed_device_data_path]
+    if { [file exists $devices_path] } {
+	dg-runtest $name "" "$default_cflags"
+    } else {
+	set shorter_name "$subdir/[file tail $name]"
+	verbose -log "$shorter_name not supported, $devices_path doesn't exist."
+	unsupported $shorter_name
+    }
+    msp430_hide_installed_devices_data 
+}
+
 # Load support procs.
 load_lib gcc-dg.exp
 
@@ -245,18 +310,21 @@ if [info exists DEFAULT_CFLAGS] then {
 # Initialize `dg'.
 dg-init
 
-msp430_set_old_board_info_ldflags
+msp430_hide_installed_devices_data
 
 # Main loop.
 dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.\[cCS\]]] \
     "" "$MSP430_DEFAULT_CFLAGS"
 
+msp430_set_old_board_info_ldflags
+
 msp430_device_permutations_runtest \
     [lsort [glob -nocomplain $srcdir/$subdir/devices/*.\[cCS\]]] \
     "$MSP430_DEFAULT_CFLAGS"
 
 # Reset any possibly changed ldflags by dg-msp-options
 msp430_reset_board_info_ldflags
+msp430_restore_installed_devices_data 
 
 # All done.
 dg-finish
-- 
2.7.4

