gollvm: initial cmake rules for gotools (cmd/go etc)

Initial rules for building the gotools programs. Does not
include check target yet.

Change-Id: I288efc7616d4acceb3a4b2f4bf2426de273627ae
Reviewed-on: https://go-review.googlesource.com/92615
Reviewed-by: Cherry Zhang <cherryyz@google.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 24520dc..9f73ae4 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -90,3 +90,6 @@
 
 # Go standard library
 add_subdirectory(libgo)
+
+# Go tools (go, gofmt, etc)
+add_subdirectory(gotools)
diff --git a/cmake/AutoGenGo.cmake b/cmake/AutoGenGo.cmake
index 931dd2d..c32d614 100644
--- a/cmake/AutoGenGo.cmake
+++ b/cmake/AutoGenGo.cmake
@@ -267,25 +267,45 @@
 #
 # Unnamed parameters:
 #
+#   * package to use for generated Go code
 #   * output file to target
 #   * path to gollvm driver binary
 #   * C compiler path
 #   * C++ compiler path
 #
-function(mkzdefaultcc outfile driverpath ccpath cxxpath)
+# Named parameters:
+#
+# EXPORT    Generated public functions (ex: DefaultCC not defaultCC).
+#
+function(mkzdefaultcc package outfile driverpath ccpath cxxpath)
+  CMAKE_PARSE_ARGUMENTS(ARG "EXPORT" "" "" ${ARGN})
+
   file(REMOVE ${outfile})
-  file(WRITE ${outfile} "package cfg\n\n")
+  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.
 
-  file(APPEND ${outfile} "func DefaultGCCGO(goos, goarch string) string { return \"${driverpath}\" }\n")
-  file(APPEND ${outfile} "func DefaultCC(goos, goarch string) string { return \"${ccpath}\" }\n")
-  file(APPEND ${outfile} "func DefaultCXX(goos, goarch string) string { return \"${cxxpath}\" }\n")
-  file(APPEND ${outfile} "const DefaultPkgConfig = \"pkg-config\"\n")
-  file(APPEND ${outfile} "var OSArchSupportsCgo = map[string]bool{}\n")
+  set(f1 "defaultGCCGO")
+  set(f2 "defaultCC")
+  set(f3 "defaultCXX")
+  set(f4 "defaultPkgConfig")
+  set(v1 "oSArchSupportsCgo")
+  if( ${ARG_EXPORT} )
+    upperfirst(${f1} "f1")
+    upperfirst(${f2} "f2")
+    upperfirst(${f3} "f3")
+    upperfirst(${f4} "f4")
+    upperfirst(${v1} "v1")
+  endif()
+
+  file(APPEND ${outfile} "func ${f1}(goos, goarch string) string { return \"${driverpath}\" }\n")
+  file(APPEND ${outfile} "func ${f2}(goos, goarch string) string { return \"${ccpath}\" }\n")
+  file(APPEND ${outfile} "func ${f3}(goos, goarch string) string { return \"${cxxpath}\" }\n")
+  file(APPEND ${outfile} "const ${f4} = \"pkg-config\"\n")
+  file(APPEND ${outfile} "var ${v1} = map[string]bool{}\n")
 endfunction()
 
 #----------------------------------------------------------------------
diff --git a/cmake/GoProgram.cmake b/cmake/GoProgram.cmake
new file mode 100644
index 0000000..09ad398
--- /dev/null
+++ b/cmake/GoProgram.cmake
@@ -0,0 +1,49 @@
+
+# This function adds a target for a specific Go program (executable),
+# for example "gofmt" or "cgo".
+#
+# Example usage:
+#
+# add_go_program(name target libgodir destdir
+#                GOSRC .../x.go .../y.go ... GOLIB abc.a foo.so ...)
+#
+# Unnamed parameters:
+#
+#   * Program name, e.g. "gofmt"
+#   * Target name, e.g. "gotools_cmd_go"
+#   * Directory containing libgo build artifacts (ex: runtime.o)
+#   * Destination directory for program build artifacts (e.g. executable)
+#
+# Named parameters:
+#
+# GOSRC     Full paths of go source files to build.
+# GOLIB     Libraries to link against.
+# GODEP     Targets on which this program should be dependent.
+# GOCFLAGS  Additional arguments passed to Go compiler.
+
+function(add_go_program progname target libgodir destdir)
+  CMAKE_PARSE_ARGUMENTS(ARG "" "" "GOSRC;GOLIB;GODEP;GOCFLAGS" ${ARGN})
+
+  # Target of build
+  set(program_exe "${destdir}/${progname}")
+
+  # Deps
+  set(deps ${ARG_GOSRC})
+  list(APPEND deps ${ARG_GODEP})
+
+  # Command to build executable.
+  add_custom_command(
+    OUTPUT ${program_exe}
+    COMMAND "${gocompiler}" "-o" ${program_exe} ${ARG_GOSRC} ${ARG_GOCFLAGS}
+            -I ${libgodir} -L ${libgodir} ${ARG_GOLIB}
+    DEPENDS ${deps}
+    COMMENT "Building go program ${progname}"
+    VERBATIM)
+
+  # Create target
+  add_custom_target(${target} ALL DEPENDS ${program_exe})
+  set_target_properties(${target} PROPERTIES FOLDER "Tools")
+
+  # TODO: add install rules
+
+endfunction()
diff --git a/gotools/CMakeLists.txt b/gotools/CMakeLists.txt
new file mode 100644
index 0000000..1eaa67e
--- /dev/null
+++ b/gotools/CMakeLists.txt
@@ -0,0 +1,88 @@
+
+# Gotools build requires libgo; if libgo is stubbed out then don't
+# try to build gotools either.
+
+if(DISABLE_LIBGO_BUILD)
+  return()
+endif()
+
+include(GoProgram)
+
+message(STATUS "starting gotools configuration.")
+
+include(GoVars)
+
+# FIXME: rewrite this to locate llvm-goparse in a more official way
+# set(driver "${bin}/../bin/llvm-goparse")
+get_target_property(driverdir llvm-goparse RUNTIME_OUTPUT_DIRECTORY)
+set(gollvm_driver "${driverdir}/llvm-goparse")
+#set(gocompiler ${gollvm_driver})
+
+# FIXME: still need to use wrapper for compilation, since
+# llvm-goparse functionality not yet complete.
+set(gocompiler "gccgo")
+
+set(cmd_srcroot "${GOLLVM_SOURCE_DIR}/gofrontend/libgo/go/cmd")
+
+set(gotools_binroot "${CMAKE_CURRENT_BINARY_DIR}")
+set(libgo_binroot "${CMAKE_CURRENT_BINARY_DIR}/../libgo")
+
+set(libgotool_archive "${libgo_binroot}/libgotool.a")
+
+# Cgo needs a copy of the defaultCC function.
+set(cgozdefaultccdotgo "${gotools_binroot}/zdefaultcc.go")
+set(cgozdefaultcctmp "${gotools_binroot}/zdefaultcc.go.tmp")
+mkzdefaultcc("main" ${cgozdefaultcctmp} ${gollvm_driver}
+             ${CMAKE_C_COMPILER} ${CMAKE_CXX_COMPILER})
+copy_if_different(${cgozdefaultcctmp} ${cgozdefaultccdotgo})
+
+set(libgo_scriptroot "${GOLLVM_SOURCE_DIR}/gofrontend/libgo")
+set(matchdotsh "${libgo_scriptroot}/match.sh")
+
+set(cgo_extra_go_files "${cgozdefaultccdotgo}")
+
+# Loop over each of the tools of interest.
+set(tools "go" "gofmt" "cgo" "vet" "buildid" "test2json")
+set(allgotools)
+foreach(tool ${tools})
+
+  # Check for tool dir
+  if(NOT EXISTS "${cmd_srcroot}/${tool}")
+    message(SEND_ERROR "Go tool directory ${tool} does not exist.")
+  else()
+    set(tool_target "gotools_cmd_${tool}")
+
+    # Invoke match.sh to collect Go files of interest for this
+    # tool, via shell script. Read result into variable.
+    execute_process(COMMAND "${shell}" "${matchdotsh}"
+      "--goarch=${goarch}" "--goos=${goos}"
+      "--srcdir=${cmd_srcroot}/${tool}"
+      OUTPUT_VARIABLE toolfiles
+      ERROR_VARIABLE errmsg
+      RESULT_VARIABLE exitstatus)
+    if(NOT ${exitstatus} MATCHES 0)
+      message(FATAL_ERROR "match.sh invocation failed: ${errmsg}")
+    endif()
+    string(STRIP ${toolfiles} toolfiles)
+    separate_arguments(toolfiles)
+
+    # Incorporate extras.
+    if(NOT "${${tool}_extra_go_files}" STREQUAL "")
+      list(APPEND toolfiles "${${tool}_extra_go_files}")
+    endif()
+
+    # Create target for program.
+    add_go_program(${tool} ${tool_target}
+      ${libgo_binroot} ${gotools_binroot}
+      GOSRC ${toolfiles}
+      GOLIB ${libgotool_archive}
+      GODEP libgotool libgo_shared libgo_static)
+    list(APPEND allgotools ${tool_target})
+  endif()
+endforeach()
+
+add_custom_target(gotools_all DEPENDS ${allgotools})
+
+# FIXME: add install rules
+
+message(STATUS "gotools configuration complete.")
diff --git a/libgo/CMakeLists.txt b/libgo/CMakeLists.txt
index dd15b20..e679320 100644
--- a/libgo/CMakeLists.txt
+++ b/libgo/CMakeLists.txt
@@ -192,11 +192,10 @@
 # Rules for zdefaultcc.go
 set(zdefaultccdotgo "${libgo_binroot}/zdefaultcc.go")
 set(zdefaultcctmp "${libgo_binroot}/zdefaultcc.go.tmp")
-mkzdefaultcc(${zdefaultcctmp} ${gollvm_driver}
-             ${CMAKE_C_COMPILER} ${CMAKE_CXX_COMPILER})
+mkzdefaultcc("cfg" ${zdefaultcctmp} ${gollvm_driver}
+             ${CMAKE_C_COMPILER} ${CMAKE_CXX_COMPILER} EXPORT)
 copy_if_different(${zdefaultcctmp} ${zdefaultccdotgo})
 
-
 # FIXME: Use a rule that copies in an archived copy of this source
 # file, since for the moment there is no clang support for the
 # -fdump-go-spec option (better to implement that as a bitcode pass,