github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/snap/plugins/x-dep.py (about) 1 # -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*- 2 # 3 # Copyright (C) 2018 Canonical Ltd 4 # 5 # This program is free software: you can redistribute it and/or modify 6 # it under the terms of the GNU General Public License version 3 as 7 # published by the Free Software Foundation. 8 # 9 # This program is distributed in the hope that it will be useful, 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 # GNU General Public License for more details. 13 # 14 # You should have received a copy of the GNU General Public License 15 # along with this program. If not, see <http://www.gnu.org/licenses/>. 16 17 """The dep plugin can be used for dep-enabled go projects. 18 19 This plugin uses the common plugin keywords as well as those for "sources". 20 For more information check the 'plugins' topic for the former and the 21 'sources' topic for the latter. 22 23 Additionally, this plugin uses the following plugin-specific keywords: 24 25 - go-packages: 26 (list of strings) 27 Go packages to build/install, these must be a "main" package. 28 Dependencies should have already been retrieved. 29 Packages that are not "main" will not cause an error, but would 30 not be useful either. 31 32 - go-importpath: 33 (string) 34 This entry tells the checked out `source` to live within a certain path 35 within `GOPATH`. This is required in order to work with absolute imports 36 and import path checking. 37 """ 38 39 import logging 40 import os 41 import shutil 42 43 import snapcraft 44 from snapcraft import common 45 46 47 logger = logging.getLogger(__name__) 48 49 50 class DepPlugin(snapcraft.BasePlugin): 51 @classmethod 52 def schema(cls): 53 schema = super().schema() 54 schema["properties"]["go-importpath"] = {"type": "string"} 55 schema["properties"]["go-packages"] = { 56 "type": "array", 57 "minitems": 1, 58 "uniqueItems": True, 59 "items": {"type": "string"}, 60 "default": [], 61 } 62 63 # The import path must be specified. 64 schema["required"].append("go-importpath") 65 66 return schema 67 68 @classmethod 69 def get_build_properties(cls): 70 # Inform Snapcraft of the properties associated with building. If these 71 # change in the YAML Snapcraft will consider the build step dirty. 72 return ["go-packages"] 73 74 @classmethod 75 def get_pull_properties(cls): 76 # Inform Snapcraft of the properties associated with pulling. If these 77 # change in the YAML Snapcraft will consider the pull step dirty. 78 return ["go-importpath"] 79 80 def __init__(self, name, options, project): 81 super().__init__(name, options, project) 82 self.build_packages.extend(["golang-go", "git"]) 83 self._gopath = os.path.join(self.partdir, "go") 84 self._gopath_src = os.path.join(self._gopath, "src") 85 self._gopath_bin = os.path.join(self._gopath, "bin") 86 self._gopath_pkg = os.path.join(self._gopath, "pkg") 87 self._path_in_gopath = os.path.join(self._gopath_src, self.options.go_importpath) 88 89 def pull(self): 90 super().pull() 91 92 try: 93 shutil.rmtree(os.path.dirname(self._path_in_gopath)) 94 except: # noqa: E722 95 pass 96 finally: 97 os.makedirs(os.path.dirname(self._path_in_gopath), exist_ok=True) 98 99 shutil.copytree(self.sourcedir, self._path_in_gopath, symlinks=True, ignore_dangling_symlinks=True) 100 101 # Fetch and run dep 102 logger.info("Fetching dep...") 103 self._run(["go", "get", "github.com/golang/dep/cmd/dep"]) 104 105 logger.info("Obtaining project dependencies...") 106 self._run( 107 [ 108 "dep", 109 "ensure", 110 "-vendor-only", 111 ] 112 ) 113 114 def clean_pull(self): 115 super().clean_pull() 116 117 # Remove the gopath (if present) 118 if os.path.exists(self._gopath): 119 shutil.rmtree(self._gopath) 120 121 def build(self): 122 super().build() 123 124 for go_package in self.options.go_packages: 125 self._run(["go", "install", go_package]) 126 if not self.options.go_packages: 127 self._run(["go", "install", "./{}/...".format(self.options.go_importpath)]) 128 129 install_bin_path = os.path.join(self.installdir, "bin") 130 os.makedirs(install_bin_path, exist_ok=True) 131 os.makedirs(self._gopath_bin, exist_ok=True) 132 for binary in os.listdir(self._gopath_bin): 133 # Skip dep. It serves no purpose in production. 134 if binary == "dep": 135 continue 136 137 binary_path = os.path.join(self._gopath_bin, binary) 138 shutil.copy2(binary_path, install_bin_path) 139 140 def clean_build(self): 141 super().clean_build() 142 143 if os.path.isdir(self._gopath_bin): 144 shutil.rmtree(self._gopath_bin) 145 146 if os.path.isdir(self._gopath_pkg): 147 shutil.rmtree(self._gopath_pkg) 148 149 def _run(self, cmd, **kwargs): 150 env = self._build_environment() 151 return self.run(cmd, cwd=self._path_in_gopath, env=env, **kwargs) 152 153 def _build_environment(self): 154 env = os.environ.copy() 155 env["GOPATH"] = self._gopath 156 env["GOBIN"] = self._gopath_bin 157 158 # Add $GOPATH/bin so dep is actually callable. 159 env["PATH"] = "{}:{}".format( 160 os.path.join(self._gopath, "bin"), env.get("PATH", "") 161 ) 162 163 include_paths = [] 164 for root in [self.installdir, self.project.stage_dir]: 165 include_paths.extend( 166 common.get_library_paths(root, self.project.arch_triplet) 167 ) 168 169 flags = common.combine_paths(include_paths, "-L", " ") 170 env["CGO_LDFLAGS"] = "{} {} {}".format( 171 env.get("CGO_LDFLAGS", ""), flags, env.get("LDFLAGS", "") 172 ) 173 174 return env