1use std::error;
2use std::fs::{read_to_string,create_dir_all};
3use std::path::Path;
4use std::path::PathBuf;
5use log::{info};
6use reqwest::Url;
7use crate::{init_classpath};
8use crate::util;
9use crate::config::{Config,Key,Error};
10use crate::jvm::{Jvm};
11use crate::package::{Dependency, PackageResolver};
12use crate::platform;
13use crate::platform::{Instance,JavaInstance};
14
15pub static PACKAGE_NAME : Key = Key::new(&["package","name"]);
20pub static PACKAGE_AUTHORS : Key = Key::new(&["package","authors"]);
21pub static PACKAGE_VERSION : Key = Key::new(&["package","version"]);
22pub static BUILD_PLATFORMS : Key = Key::new(&["build","platforms"]);
23pub static DEPENDENCIES : Key = Key::new(&["dependencies"]);
24
25const PACKAGE_CENTRAL : &str = "https://github.com/Whiley/Repository/raw/master/";
27
28#[derive(Debug)]
33pub enum Kind {
34 Warning,
36 SyntaxError,
38 InternalFailure
40}
41
42#[derive(Debug)]
43pub struct Marker {
44 kind: Kind,
45 path: PathBuf,
46 start: usize,
47 end: usize,
48 message: String
49}
50
51impl Marker {
52 pub fn new(kind: Kind, path: PathBuf, start: usize, end: usize, message: String) -> Self {
53 Marker{kind,path,start,end,message}
54 }
55 pub fn enclosing_line(&self) -> Result<Line,Box<dyn error::Error>> {
57 let contents = read_to_string(self.path.as_path())?;
59 let mut line = 1;
61 for l in util::line_offsets(contents.as_str()) {
63 if l.contains(self.start) {
64 return Ok(Line{offset:l.start,line,contents:l.as_str().to_string()});
65 }
66 line = line + 1;
67 }
68 panic!("No enclosing line!");
70 }
71}
72
73pub struct Line {
74 pub offset: usize,
76 pub line: usize,
78 pub contents: String
80}
81
82pub struct Build {
89 pub name: String,
90 pub authors: Vec<String>,
91 pub version: String,
92 pub platforms: Vec<platform::Instance>,
95 pub dependencies: Vec<Dependency>
97}
98
99impl Build {
100 pub fn from_str<'a>(config: &Config, whileyhome: &Path, registry: &'a platform::Registry<'a>) -> Result<Build,Error> {
102 let name = config.get_string(&PACKAGE_NAME)?;
104 let authors = config.get_string_array(&PACKAGE_AUTHORS)?;
105 let version = config.get_string(&PACKAGE_VERSION)?;
106 let platforms = config.get_string_array(&BUILD_PLATFORMS)?;
107 let deps = config.get_strings(&DEPENDENCIES).unwrap_or(Vec::new());
108 let mut ps = Vec::new();
110 for p in &platforms {
111 let init = match registry.get(p) {
112 None => {
113 return Err(Error::UnknownPlatform(p.to_string()));
114 }
115 Some(v) => v
116 };
117 ps.push(init.apply(config,whileyhome)?);
118 }
119 let dependencies = deps.into_iter().map(|(k,v)| Dependency::new(k,v)).collect();
121 return Ok(Build{name,authors,version,platforms:ps,dependencies});
123 }
124
125 pub fn manifest(&self) -> Manifest {
128 Manifest::new(self)
129 }
130
131 pub fn run(&self, whileyhome: &Path) -> Result<bool,Box<dyn error::Error>> {
133 self.initialise(whileyhome)?;
135 for p in &self.platforms {
137 let result = match p {
139 Instance::Java(i) => {
140 self.run_java(i.as_ref(),whileyhome)
141 },
142 Instance::Rust(_) => {
143 todo!("Rust platforms not currently supported")
144 }
145 };
146 match result {
148 Ok(markers) => {
149 if markers.len() > 0 {
150 for m in markers {
151 let l = m.enclosing_line()?;
153 let f = m.path.into_os_string().into_string().unwrap();
154 println!("{}:{}:{}",f,l.line,m.message);
156 println!("{}",l.contents);
158 let padding = " ".repeat(m.start - l.offset);
159 let highlight = "^".repeat(m.end - m.start + 1);
160 println!("{}{}",padding,highlight);
161 }
162 return Ok(false);
164 }
165 }
166 Err(out) => {
167 println!("{}",out);
168 return Ok(false);
170 }
171 }
172 }
173 Ok(true)
175 }
176
177 fn run_java(&self, i: &dyn JavaInstance, whileyhome: &Path) -> Result<Vec<Marker>,Box<dyn error::Error>> {
179 let cp = init_classpath(&whileyhome,i.dependencies())?;
182 let jvm = Jvm::new(cp,vec![("WHILEYHOME",&whileyhome)]);
184 let args : Vec<String> = i.arguments();
186 let str_args : Vec<&str> = args.iter().map(String::as_str).collect();
188 info!("Executing java {:?}",str_args);
190 let output = jvm.exec(&str_args);
192 info!("Java output \"{}\"",output.as_str());
194 i.process(output.as_str())
196 }
197
198 fn initialise(&self, whileyhome: &Path) -> Result<(),Box<dyn error::Error>> {
201 self.create_binary_folders()?;
202 self.resolve_packages(whileyhome)?;
204 Ok(())
206 }
207
208 fn create_binary_folders(&self) -> Result<(),Box<dyn error::Error>> {
210 for ba in self.manifest() {
212 match ba {
214 Artifact::BinaryFolder(p) => {
215 if !p.as_path().exists() {
216 info!("Making binary folder {}",p.display());
217 create_dir_all(p)?;
218 }
219 }
220 _ => {
221 }
222 };
223 }
224 Ok(())
226 }
227
228 fn resolve_packages(&self, whileyhome: &Path) -> Result<(),Box<dyn error::Error>> {
232 let mut repo = PathBuf::from(whileyhome);
234 repo.push("repository");
235 let base_url = Url::parse(PACKAGE_CENTRAL).unwrap();
237 let resolver = PackageResolver::new(repo, base_url);
239 resolver.resolve(&self.dependencies)?;
241 Ok(())
243 }
244}
245
246#[derive(Debug)]
251pub enum Artifact {
252 SourceFile(PathBuf),
253 SourceFolder(PathBuf),
254 BinaryFile(PathBuf, bool),
255 BinaryFolder(PathBuf)
256}
257
258pub struct Manifest {
259 artifacts: Vec<Artifact>
260}
261
262impl Manifest {
263 pub fn new(b: &Build) -> Manifest {
264 let mut artifacts = Vec::new();
265 artifacts.push(Artifact::SourceFile(PathBuf::from("wy.toml")));
266 for i in &b.platforms {
268 artifacts.extend(i.manifest());
270 }
271 Manifest{artifacts}
273 }
274}
275
276impl IntoIterator for Manifest {
277 type Item = Artifact;
278 type IntoIter = std::vec::IntoIter<Self::Item>;
279
280 fn into_iter(self) -> Self::IntoIter {
281 self.artifacts.into_iter()
282 }
283}