gollvm: support installation targets

Add support for installing a build. The main install target of
interest is 'install-gollvm', which will install compiler, libgo, and
gotools.

Change-Id: I6aa050996f86332318338b6988668267675719b0
Reviewed-on: https://go-review.googlesource.com/112358
Reviewed-by: Cherry Zhang <cherryyz@google.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6c25321..ccab33d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -13,6 +13,7 @@
 list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
 
 include(CmakeUtils)
+include(AddGollvm)
 
 # So that we can issue "make -jN" cmds in externalproject_add
 processorcount(PROCESSOR_COUNT)
@@ -67,6 +68,10 @@
   LOG_INSTALL 1
   )
 
+# Top level targets for building, installing
+add_custom_target(gollvm ALL)
+add_custom_target(install-gollvm)
+
 # In most use cases, we want to force a rebuild of all objects built
 # from Go source if the compiler changes.
 set(gocdep llvm-goc llvm-goc-token)
diff --git a/cmake/modules/AddGollvm.cmake b/cmake/modules/AddGollvm.cmake
new file mode 100644
index 0000000..954965c
--- /dev/null
+++ b/cmake/modules/AddGollvm.cmake
@@ -0,0 +1,66 @@
+
+# Library subdir within installation.
+# FIXME: select 32/64 based on default target triple
+set(libsubdir "lib64")
+
+# Set version number string (used by install rules)
+set(libversion ${LLVM_VERSION_MAJOR})
+if (NOT ${LLVM_VERSION_MINOR} EQUAL 0 AND NOT ${LLVM_VERSION_PATCH} EQUAL 0)
+  set(libversion "${libversion}.${LLVM_VERSION_MINOR}")
+endif()
+if (NOT ${LLVM_VERSION_PATCH} EQUAL 0)
+  set(libversion "${libversion}.${LLVM_VERSION_PATCH}")
+endif()
+
+# These are incorporated into GollvmConfig.h
+set(GOLLVM_LIBVERSION "${libversion}")
+set(GOLLVM_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}")
+set(GOLLVM_INSTALL_LIBDIR "${CMAKE_INSTALL_PREFIX}/${libsubdir}")
+
+macro(add_gollvm_library name)
+  llvm_add_library(${name} ${ARGN})
+
+  # Configure for install.
+  install(TARGETS ${name}
+    COMPONENT ${name}
+    LIBRARY DESTINATION ${libsubdir}
+    ARCHIVE DESTINATION ${libsubdir}
+    RUNTIME DESTINATION bin)
+
+  # Add an install target.
+  add_custom_target(install-${name}
+    DEPENDS ${name}
+    COMMAND "${CMAKE_COMMAND}"
+    -DCMAKE_INSTALL_COMPONENT=${name}
+    -P "${CMAKE_BINARY_DIR}/cmake_install.cmake")
+  add_dependencies(install-gollvm install-${name})
+
+  set_target_properties(${name} PROPERTIES FOLDER "Gollvm libraries")
+endmacro(add_gollvm_library)
+
+macro(add_gollvm_executable name)
+  add_llvm_executable(${name} ${ARGN} )
+  set_target_properties(${name} PROPERTIES FOLDER "Gollvm executables")
+endmacro(add_gollvm_executable)
+
+macro(add_gollvm_tool name)
+
+  add_gollvm_executable(${name} ${ARGN})
+
+  # Configure for install.
+  install(TARGETS ${name}
+    COMPONENT ${name}
+    LIBRARY DESTINATION ${libsubdir}
+    ARCHIVE DESTINATION ${libsubdir}
+    RUNTIME DESTINATION bin)
+
+  # Add an install target.
+  add_custom_target(install-${name}
+    DEPENDS ${name}
+    COMMAND "${CMAKE_COMMAND}"
+    -DCMAKE_INSTALL_COMPONENT=${name}
+    -P "${CMAKE_BINARY_DIR}/cmake_install.cmake")
+  add_dependencies(install-gollvm install-${name})
+
+  set_target_properties(${name} PROPERTIES FOLDER "Gollvm tools")
+endmacro()
diff --git a/cmake/modules/AutoGenGo.cmake b/cmake/modules/AutoGenGo.cmake
index ab5d51f..a93df0f 100644
--- a/cmake/modules/AutoGenGo.cmake
+++ b/cmake/modules/AutoGenGo.cmake
@@ -18,13 +18,14 @@
   file(REMOVE ${outfile})
   file(WRITE ${outfile} "package sys\n")
 
-  # FIXME: normally DefaultGoRoot is set to the --prefix option used
-  # when configuring (assuming that "configure" is run), but with
-  # cmake it's possible to do a build first and then later on install
-  # that build with a second run of cmake. For this reason looking at
-  # things like CMAKE_INSTALL_PREFIX doesn't make much sense (it will
-  # simply be set to /usr/local). This area needs some work.
-  file(APPEND ${outfile} "func init() { DefaultGoroot = \"${bindir}\" }\n")
+  # FIXME: typical LLVM usage allows for performing a build without
+  # CMAKE_INSTALL_PREFIX set, then doing an install from the build
+  # using a newly chosen CMAKE_INSTALL_PREFIX value. This mode is
+  # not currently supported -- the install prefix has to be set properly
+  # as part of the original build.
+
+  # Tools subdir within the install.
+  file(APPEND ${outfile} "func init() { DefaultGoroot = \"${CMAKE_INSTALL_PREFIX}\" }\n")
 
   # Compiler version
   file(STRINGS "${srcroot}/../VERSION" rawver)
@@ -39,7 +40,7 @@
   # executables. Where things like "cgo" will live hasn't been ironed
   # out yet, so just pick a spot in the bin directory for now. See also
   # 'DefaultGoRoot' above.
-  file(APPEND ${outfile} "const GccgoToolDir = \"${bindir}\"\n")
+  file(APPEND ${outfile} "const GccgoToolDir = \"${CMAKE_INSTALL_PREFIX}/tools\"\n")
 
   # FIXME: add a real switch base on configuration params here.
 
@@ -200,9 +201,7 @@
   file(WRITE ${outfile} "package objabi\n\n")
   file(APPEND ${outfile} "import \"runtime\"\n")
 
-  # FIXME: see the comment in mkversion above relating to the GoRoot
-  # setting (we may not know installation dir at time of cmake run).
-  file(APPEND ${outfile} "func init() { defaultGOROOT = \"${binroot}\" }\n")
+  file(APPEND ${outfile} "func init() { defaultGOROOT = \"${GOLLVM_INSTALL_DIR}\" }\n")
 
   file(APPEND ${outfile} "const defaultGO386 = `sse2`\n")
   file(APPEND ${outfile} "const defaultGOARM = `5`\n")
@@ -245,7 +244,6 @@
 #
 #   * package to use for generated Go code
 #   * output file to target
-#   * path to gollvm driver binary
 #   * C compiler path
 #   * C++ compiler path
 #
@@ -253,17 +251,15 @@
 #
 # EXPORT    Generated public functions (ex: DefaultCC not defaultCC).
 #
-function(mkzdefaultcc package outfile driverpath ccpath cxxpath)
+function(mkzdefaultcc package outfile ccpath cxxpath)
   CMAKE_PARSE_ARGUMENTS(ARG "EXPORT" "" "" ${ARGN})
 
+  # Construct default driver path
+  set(driverpath "${GOLLVM_INSTALL_DIR}/bin/llvm-goc")
+
   file(REMOVE ${outfile})
   file(WRITE ${outfile} "package ${package}\n\n")
 
-  # FIXME: once again, this is a problematic function since in theory
-  # we don't yet know yet where things are going to be installed. For
-  # the time being, just use that paths of the host build C/C++
-  # compiler and the gollvm driver executable.
-
   set(f1 "defaultGCCGO")
   set(f2 "defaultCC")
   set(f3 "defaultCXX")
diff --git a/cmake/modules/GoPackage.cmake b/cmake/modules/GoPackage.cmake
index c22a419..7e9e065 100644
--- a/cmake/modules/GoPackage.cmake
+++ b/cmake/modules/GoPackage.cmake
@@ -105,12 +105,27 @@
   # Create target
   add_custom_target(${pkgtarget} ALL DEPENDS ${pkg_outputs})
   set_target_properties(${pkgtarget} PROPERTIES FOLDER "Object Libraries")
+  add_dependencies(gollvm ${pkgtarget})
 
   # Caller needs to know these.
   set(package_ofile ${package_ofile} PARENT_SCOPE)
   set(package_picofile ${package_picofile} PARENT_SCOPE)
   set(package_goxfile ${package_goxfile} PARENT_SCOPE)
 
-  # TODO: add install rules
+  # *.gox files are installed in <lib>/go/<ver>/<triple>
+  set(goxinstalldest "lib${library_suffix}/go/${libversion}/${LLVM_DEFAULT_TARGET_TRIPLE}/${pdir}")
+
+  # Configure for install.
+  install(PROGRAMS ${package_goxfile}
+    COMPONENT ${pkgtarget}
+    DESTINATION ${goxinstalldest})
+
+  # Add an install target.
+  add_custom_target(install-${pkgtarget}
+    DEPENDS ${pkgtarget}
+    COMMAND "${CMAKE_COMMAND}"
+    -DCMAKE_INSTALL_COMPONENT=${pkgtarget}
+    -P "${CMAKE_BINARY_DIR}/cmake_install.cmake")
+  add_dependencies(install-gollvm install-${pkgtarget})
 
 endfunction()
diff --git a/cmake/modules/GoProgram.cmake b/cmake/modules/GoProgram.cmake
index 98fa81f..c20fb77 100644
--- a/cmake/modules/GoProgram.cmake
+++ b/cmake/modules/GoProgram.cmake
@@ -20,9 +20,10 @@
 # GOLIB     Libraries to link against.
 # GODEP     Targets on which this program should be dependent.
 # GOCFLAGS  Additional arguments passed to Go compiler.
+# ISUBDIR   Installation subdir (e.g. "tools" or "bin")
 
 function(add_go_program progname target libgodir destdir)
-  CMAKE_PARSE_ARGUMENTS(ARG "" "" "GOSRC;GOLIB;GODEP;GOCFLAGS" ${ARGN})
+  CMAKE_PARSE_ARGUMENTS(ARG "" "" "GOSRC;GOLIB;GODEP;GOCFLAGS;ISUBDIR" ${ARGN})
 
   set(object "${progname}_.o")
 
@@ -38,7 +39,7 @@
     OUTPUT ${object}
     COMMAND "${gocompiler}" "-c" "-o" ${object} ${ARG_GOSRC} ${ARG_GOCFLAGS}
             -I ${libgodir} -L ${libgodir}
-    DEPENDS ${deps}
+    DEPENDS ${deps} ${gocdep}
     COMMENT "Building object for go program ${progname}"
     VERBATIM)
 
@@ -53,8 +54,20 @@
 
   # Create target
   add_custom_target(${target} ALL DEPENDS ${program_exe})
-  set_target_properties(${target} PROPERTIES FOLDER "Tools")
 
-  # TODO: add install rules
+  # Configure for install.
+  install(PROGRAMS ${program_exe}
+    COMPONENT ${target}
+    DESTINATION ${ARG_ISUBDIR})
+
+  # Add an install target.
+  add_custom_target(install-${target}
+    DEPENDS ${target}
+    COMMAND "${CMAKE_COMMAND}"
+    -DCMAKE_INSTALL_COMPONENT=${target}
+    -P "${CMAKE_BINARY_DIR}/cmake_install.cmake")
+  add_dependencies(install-gollvm install-${target})
+
+  set_target_properties(${target} PROPERTIES FOLDER "Gollvm gotools")
 
 endfunction()
diff --git a/cmake/modules/GoVars.cmake b/cmake/modules/GoVars.cmake
index 4fa05a1..f6735cb 100644
--- a/cmake/modules/GoVars.cmake
+++ b/cmake/modules/GoVars.cmake
@@ -2,12 +2,14 @@
 # Set goarch/goos based on what we see in the LLVM triple.
 string(REGEX REPLACE "-" " " lht_components ${LLVM_DEFAULT_TARGET_TRIPLE})
 separate_arguments(lht_components)
-list(GET lht_components 0 goarch)
+list(GET lht_components 0 llarch)
 list(GET lht_components 2 goos)
 
 # LLVM's "x86_64" is the same as Go's "amd64".
-if( ${goarch} STREQUAL "x86_64" )
+if( ${llarch} STREQUAL "x86_64" )
   set(goarch "amd64")
+else()
+  message(SEND_ERROR "Arch ${llarch} not yet supported")
 endif()
 
 # List of all architectures, families, os flavors.
@@ -15,6 +17,13 @@
 set(allgoarchfamily "I386" "ALPHA" "AMD64" "ARM" "ARM64" "IA64" "M68K" "MIPS" "MIPS64" "PPC" "PPC64" "S390" "S390X" "SPARC" "SPARC64")
 set(allgoos "aix" "android" "darwin" "dragonfly" "freebsd" "irix" "linux" "netbsd" "openbsd" "plan9" "rtems" "solaris" "windows")
 
+# Set library suffix based on target triple
+if( ${llarch} STREQUAL "x86_64" )
+  set(library_suffix "64")
+else()
+  message(SEND_ERROR "Arch ${llarch} not yet supported")
+endif()
+
 # FIXME: write code to insure that this is set and that the shell
 # in question behaves properly.
 set(shell $ENV{SHELL})
diff --git a/driver-main/CMakeLists.txt b/driver-main/CMakeLists.txt
index 9ac9449..685b5ac 100644
--- a/driver-main/CMakeLists.txt
+++ b/driver-main/CMakeLists.txt
@@ -30,7 +30,7 @@
 include_directories(${EXTINSTALLDIR}/include)
 
 # The llvm-goc executable itself
-add_llvm_tool(llvm-goc
+add_gollvm_tool(llvm-goc
   llvm-goc.cpp)
 
 # Record the fact that llvm-goc depends on these libs
@@ -60,3 +60,7 @@
 # Token target.
 add_custom_target(llvm-goc-token ALL DEPENDS ${llvm_goc_token})
 
+# Add a symlink to llvm-goc named "gccgo" in install dir, for
+# compatibility with gccgo.
+install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink \"llvm-goc\" \"${CMAKE_INSTALL_PREFIX}/bin/gccgo\")"
+  COMPONENT llvm-goc)
diff --git a/driver/CMakeLists.txt b/driver/CMakeLists.txt
index d96d067..d1b13bd 100644
--- a/driver/CMakeLists.txt
+++ b/driver/CMakeLists.txt
@@ -7,6 +7,10 @@
 tablegen(LLVM GollvmOptions.inc -gen-opt-parser-defs)
 add_public_tablegen_target(GollvmDriverOptions)
 
+configure_file(
+    ${CMAKE_CURRENT_SOURCE_DIR}/GollvmConfig.h.cmake
+    ${CMAKE_CURRENT_BINARY_DIR}/GollvmConfig.h)
+
 # Include directories needed for this lib.
 include_directories(${GOFRONTEND_SOURCE_DIR})
 include_directories(${BRIDGE_SOURCE_DIR})
diff --git a/driver/CompileGo.cpp b/driver/CompileGo.cpp
index c4bc427..2c0b094 100644
--- a/driver/CompileGo.cpp
+++ b/driver/CompileGo.cpp
@@ -1,4 +1,4 @@
-//===-- CompileGo.cpp ------------------------------------------------------===//
+//===-- CompileGo.cpp -----------------------------------------------------===//
 //
 //                     The LLVM Compiler Infrastructure
 //
@@ -19,6 +19,7 @@
 #include "go-c.h"
 #include "mpfr.h"
 #include "GollvmOptions.h"
+#include "GollvmConfig.h"
 
 #include "Action.h"
 #include "Artifact.h"
@@ -29,6 +30,7 @@
 #include "llvm/ADT/Triple.h"
 #include "llvm/Analysis/TargetLibraryInfo.h"
 #include "llvm/Bitcode/BitcodeWriterPass.h"
+#include "llvm/Config/llvm-config.h"
 #include "llvm/IR/DiagnosticInfo.h"
 #include "llvm/IR/DiagnosticPrinter.h"
 #include "llvm/IR/IRPrintingPasses.h"
@@ -60,6 +62,8 @@
 #include "llvm/Transforms/IPO.h"
 #include "llvm/Transforms/IPO/PassManagerBuilder.h"
 
+#include <sstream>
+
 using namespace llvm;
 
 namespace gollvm {
@@ -458,14 +462,27 @@
       go_add_search_path(dir.c_str());
   }
 
-  // Library dirs
-  // TODO: add version, architecture dirs
+  // Start with list of user-provided -L directories, then append an
+  // entry corresponing to the lib dir for the install.
   std::vector<std::string> libargs =
       args_.getAllArgValues(gollvm::options::OPT_L);
+  libargs.push_back(GOLLVM_INSTALL_LIBDIR);
+
+  // Populate Go package search path based on -L options.
   for (auto &dir : libargs) {
-    struct stat st;
-    if (stat (dir.c_str(), &st) == 0 && S_ISDIR (st.st_mode))
-      go_add_search_path(dir.c_str());
+    if (!sys::fs::is_directory(dir))
+      continue;
+
+    std::stringstream b1;
+    b1 << dir << sys::path::get_separator().str() << "go"
+       << sys::path::get_separator().str() << GOLLVM_LIBVERSION;
+    if (sys::fs::is_directory(b1.str()))
+      go_add_search_path(b1.str().c_str());
+
+    std::stringstream b2;
+    b2 << b1.str() << sys::path::get_separator().str() << triple_.str();
+    if (sys::fs::is_directory(b2.str()))
+      go_add_search_path(b2.str().c_str());
   }
 
   return true;
diff --git a/driver/GnuTools.cpp b/driver/GnuTools.cpp
index a3d669e..67d10ac 100644
--- a/driver/GnuTools.cpp
+++ b/driver/GnuTools.cpp
@@ -16,6 +16,7 @@
 #include "Compilation.h"
 #include "Driver.h"
 #include "ToolChain.h"
+#include "GollvmConfig.h"
 
 #include "llvm/Option/ArgList.h"
 #include "llvm/Support/Path.h"
@@ -302,7 +303,8 @@
   }
 
   // Pick up correct directory for Go libraries.
-  std::string golib(llvm::Twine("-L" + compilation.driver().installDir() + "/../libgo/" + toolchain().driver().triple().str()).str());
+  std::string golib("-L");
+  golib += GOLLVM_INSTALL_LIBDIR;
   cmdArgs.push_back(args.MakeArgString(golib.c_str()));
 
   // Incorporate linker arguments needed for Go.
diff --git a/driver/GollvmConfig.h.cmake b/driver/GollvmConfig.h.cmake
new file mode 100644
index 0000000..bb0e56d
--- /dev/null
+++ b/driver/GollvmConfig.h.cmake
@@ -0,0 +1,17 @@
+
+#ifndef GOLLVM_CONFIG_H
+#define GOLLVM_CONFIG_H
+
+// Root of gollvm install
+#cmakedefine GOLLVM_INSTALL_DIR "@GOLLVM_INSTALL_DIR@"
+
+// Library subdir within install
+#cmakedefine GOLLVM_INSTALL_LIBDIR "@GOLLVM_INSTALL_LIBDIR@"
+
+// Library version (e.g. 7, 4.5, 8.0.1)
+#cmakedefine GOLLVM_LIBVERSION "@GOLLVM_LIBVERSION@"
+
+// Compiler version. Same as library version currently.
+#define GOLLVM_COMPILERVERSION GOLLVM_LIBVERSION
+
+#endif // GOLLVM_CONFIG_H
diff --git a/gotools/CMakeLists.txt b/gotools/CMakeLists.txt
index ad655dc..efeb26c 100644
--- a/gotools/CMakeLists.txt
+++ b/gotools/CMakeLists.txt
@@ -27,7 +27,7 @@
 # Cgo needs a copy of the defaultCC function in the "main" package.
 set(cgozdefaultccdotgo "${gotools_binroot}/zdefaultcc.go")
 set(cgozdefaultcctmp "${gotools_binroot}/zdefaultcc.go.tmp")
-mkzdefaultcc("main" ${cgozdefaultcctmp} ${gollvm_driver}
+mkzdefaultcc("main" ${cgozdefaultcctmp}
              ${CMAKE_C_COMPILER} ${CMAKE_CXX_COMPILER})
 copy_if_different(${cgozdefaultcctmp} ${cgozdefaultccdotgo})
 
@@ -73,17 +73,24 @@
       list(APPEND toolfiles "${${tool}_extra_go_files}")
     endif()
 
+    set(isubdir "tools")
+    if("${tool}" STREQUAL "go")
+      set(isubdir "bin")
+    endif()
+
     # Create target for program.
     add_go_program(${tool} ${tool_target}
       ${libgo_binroot} ${gotools_binroot}
       GOSRC ${toolfiles}
       GOLIB ${libgotool_archive}
+      ISUBDIR ${isubdir}
       GODEP libgotool libgo_shared libgo_static)
     list(APPEND allgotools ${tool_target})
   endif()
 endforeach()
 
 add_custom_target(gotools_all DEPENDS ${allgotools})
+add_dependencies(gollvm gotools_all)
 
 message(STATUS "gotools: generating check targets")
 
diff --git a/libgo/CMakeLists.txt b/libgo/CMakeLists.txt
index 83ee5df..4584503 100644
--- a/libgo/CMakeLists.txt
+++ b/libgo/CMakeLists.txt
@@ -222,7 +222,7 @@
 # Rules for zdefaultcc.go
 set(zdefaultccdotgo "${libgo_binroot}/zdefaultcc.go")
 set(zdefaultcctmp "${libgo_binroot}/zdefaultcc.go.tmp")
-mkzdefaultcc("cfg" ${zdefaultcctmp} ${gollvm_driver}
+mkzdefaultcc("cfg" ${zdefaultcctmp}
              ${CMAKE_C_COMPILER} ${CMAKE_CXX_COMPILER} EXPORT)
 copy_if_different(${zdefaultcctmp} ${zdefaultccdotgo})
 
@@ -456,7 +456,7 @@
 add_dependencies(libgo_c_nonpiclib runtimeinctarg)
 
 # Static libgo -- combines non-pic C objects and non-pic Go objects
-add_library(libgo_static STATIC
+add_gollvm_library(libgo_static STATIC
   $<TARGET_OBJECTS:libgo_c_nonpiclib>
   ${libgo_go_nonpicobjects}
   $<TARGET_OBJECTS:libbacktrace_nonpiclib>
@@ -470,7 +470,7 @@
     "${CMAKE_SHARED_LINKER_FLAGS}")
 
 # Shared libgo -- combines pic C objects and non-pic Go objects.
-add_library(libgo_shared SHARED
+add_gollvm_library(libgo_shared SHARED
   $<TARGET_OBJECTS:libgo_c_piclib>
   ${libgo_go_picobjects}
   $<TARGET_OBJECTS:libbacktrace_piclib>
@@ -484,7 +484,7 @@
 set(libgobegincfiles
   "${libgo_csrcroot}/runtime/go-main.c")
 # libgobegin.a (static library only)
-add_library(libgobegin STATIC
+add_gollvm_library(libgobegin STATIC
   ${libgobegincfiles})
 add_dependencies(libgobegin runtimeinctarg)
 target_include_directories(libgobegin PUBLIC
@@ -500,7 +500,7 @@
 set(libgolibbegincfiles
   "${libgo_csrcroot}/runtime/go-libmain.c")
 # libgolibbegin.a (static library only)
-add_library(libgolibbegin STATIC
+add_gollvm_library(libgolibbegin STATIC
   ${libgolibbegincfiles})
 add_dependencies(libgolibbegin runtimeinctarg)
 target_include_directories(libgolibbegin PUBLIC
@@ -516,6 +516,7 @@
 # Pseudo-target for all libgo buildables.
 add_custom_target(libgo_all DEPENDS
   llvm-goc libgo_static libgo_shared libgobegin libgolibbegin)
+add_dependencies(gollvm libgo_all)
 
 # Create a target-specific symlink to the Go library dir. This is
 # an interim solution; ideally we want to key off of CMAKE_INSTALL_PREFIX.