Commit 2d551a4e authored by Bat's avatar Bat
Browse files

Merge branch 'develop' into 'develop'

Extracted libpoulp: fix #49

Closes #49

See merge request !20
parents 7163d1a2 d6c5fb72
Pipeline #12488 failed with stages
in 1 minute and 43 seconds
......@@ -23,12 +23,9 @@ json-vala = "1.7.x"
"libsoup-2.4" = "*"
semver = "1.x.x"
toml-glib = "~0.1.0"
libpoulp = "0.x.x"
[scripts]
install = [
"install -m 777 build/poulp /usr/bin/"
]
# Multiple targets are not supported yet. :(
lib = [
"valac src/logger.vala src/manifest.vala src/repository.vala src/utils.vala --library=build/libpoulp -H build/libpoulp.h -X -fPIC -X -shared -o build/libpoulp.so --pkg semver-0.1.0 --pkg json-glib-1.0 --pkg gee-0.8 --pkg gio-2.0"
]
namespace Poulp {
/**
* Represents the result of an application build
*/
public class AppBuildOutput : BuildOutput {
/**
* The (base)name of the binary file
*/
public string bin_file { get; set; }
/**
* {@inheritDoc}
*/
public override void set_output (string name) {
this.name = name;
#if WIN32
this.bin_file = name + ".exe";
#else
this.bin_file = name;
#endif
}
/**
* {@inheritDoc}
*/
public override async bool install () {
try {
Manifest man = new Manifest.for_path (Path.build_filename (this.project_path, "poulp.toml"));
if (man.scripts.has_key ("pre-install")) {
return yield new ScriptRunner ().run (man.scripts["pre-install"]);
}
if (man.scripts.has_key ("install")) {
return yield new ScriptRunner ().run (man.scripts["install"]);
} else {
string install_dir = Environment.get_variable ("POULP_BIN_DIR");
File bin = File.new_for_path (Path.build_filename (this.project_path, this.build_dir, this.bin_file));
File dir = File.new_for_path (Path.build_filename (install_dir, this.bin_file));
bin.copy (dir, FileCopyFlags.OVERWRITE | FileCopyFlags.ALL_METADATA);
}
if (man.scripts.has_key ("post-install")) {
return yield new ScriptRunner ().run (man.scripts["post-install"]);
}
return true;
} catch (Error err) {
Logger.log_error (err.message);
return false;
}
}
}
}
using Gee;
namespace Poulp {
/**
* Represents the result of a build.
*/
public abstract class BuildOutput : Object {
/**
* false if build failed, true otherwise
*/
public bool success { get; set; default = true; }
/**
* The absolute path to the projects root
*/
public string project_path { get; set; }
/**
* The directory where the output can be found (`build` by default)
*/
public string build_dir { get; set; }
/**
* The name of the output
*/
public string name { get; set; }
/**
* The bsolute path to the output
*/
public string output_path {
owned get {
return Path.build_filename (this.project_path, this.build_dir, this.name);
}
}
/**
* Sets the output files for a project
*
* @param name The name of the project
*/
public abstract void set_output (string name);
/**
* Installs the output file(s)
*/
public abstract async bool install ();
}
}
using Gee;
namespace Poulp {
/**
* Builds a project
*/
public class Builder : Object {
/**
* The {@link Poulp.Manifest} of the project to build
*/
private Manifest manifest { get; set; }
/**
* Creates a {@link Poulp.Builder} for a project
*
* @param man The manifest of the project to build
*/
public Builder (Manifest man) {
this.manifest = man;
}
/**
* Builds the project
*
* @param dir The directory where the project is located (to know where to put output files)
* @param release true if we are building a release version
*/
public async BuildOutput build (string dir, bool release = false) throws BuildError {
if (this.manifest.vala_version != null) {
var vala_version = get_vala_version ();
if (!(vala_version in this.manifest.vala_version)) {
throw new BuildError.INVALID_VALA_VERSION ("Required Vala %s, but the installed version is %s."
.printf (this.manifest.vala_version.to_string (), vala_version.to_string ()));
}
}
if (this.manifest.library) {
return yield this.build_lib (dir, release);
} else {
return yield this.build_app (dir, release);
}
}
/**
* Builds a library
*
* @param dir The directory where the project is located (to know where to put output files)
* @param release true if we are building a release version
*/
public async LibBuildOutput build_lib (string dir, bool release) {
var valac_args = new ArrayList<string>.wrap ({ "valac" });
LibBuildOutput output = new LibBuildOutput ();
output.project_path = dir;
try {
output.build_dir = "build";
// gettin the output file
string name;
if (this.manifest.output != null) { // if the output is specified in the manifest, we use it
name = this.manifest.output;
} else {
name = this.manifest.name; // else, we use build/name
}
output.set_output (name);
// we add some flags and extensions that are platform specifics
#if WIN32
valac_args.add ("-D WIN32");
#elif BSD
valac_args.add ("-D BSD");
#elif DARWIN
valac_args.add ("-D DARWIN");
#endif
// we create the output directory if it doesn't exist
File out_file = File.new_for_path (name);
string out_parent = out_file.get_parent ().get_path ();
if (FileUtils.test (out_parent, FileTest.EXISTS) == false) {
DirUtils.create_with_parents (out_parent, 0755);
}
valac_args.add ("--output");
valac_args.add (Path.build_filename (output.build_dir, output.lib_file));
// add files, vapidir, and --pkg to valac arguments
if (this.manifest.vapi_dir != null) {
valac_args.add ("--vapidir");
valac_args.add (this.manifest.vapi_dir);
}
if (this.manifest.files.size == 0) {
valac_args.add ("*.vala");
} else {
foreach (string file in this.manifest.files) {
valac_args.add (@"$(file) ");
}
}
foreach (var lib in this.manifest.dependencies.entries) {
valac_args.add (@"--pkg $(lib.key)");
}
if (release) {
foreach (var dev_lib in this.manifest.dev_dependencies.entries) {
valac_args.add (@"--pkg $(dev_lib.key)");
}
}
this.add_features_flags (valac_args);
// library specific flags
valac_args.add ("--library");
valac_args.add (Path.build_filename (output.build_dir, output.name));
valac_args.add ("-H");
valac_args.add (Path.build_filename (output.build_dir, output.header_file));
valac_args.add ("-X");
valac_args.add ("-fPIC");
valac_args.add ("-X");
valac_args.add ("-shared");
foreach (string param in this.manifest.compiler_params) {
valac_args.add (param);
}
// run valac
string valac_cmd = string.joinv (" ", valac_args.to_array ());
Logger.logc (LogColor.GREEN, "Running `%s`\n", valac_cmd);
int valac_status = yield spawn_async (valac_cmd, dir);
if (valac_status == 0) {
Logger.logc (LogColor.BOLD_GREEN, "Successfully built project `%s`.", this.manifest.name);
// write .deps and .pc file if compilation succeeds
string deps = "";
foreach (var dep in this.manifest.dependencies.entries) {
deps += dep.key + "\n";
}
FileUtils.set_contents (Path.build_filename (dir, output.build_dir, output.deps_file), deps);
string include_dir = Environment.get_variable ("POULP_HEADERS_DIR");
string lib_dir = Environment.get_variable ("POULP_LIB_DIR");
string desc = this.manifest.description == null ? "No description provided" : this.manifest.description;
string pc = """includedir=%s
libdir=%s
Name: %s
Description: %s
Version: %s
CFlags: -I${includedir}%s
Libs: -L${libdir} -l%s
""".printf (include_dir, lib_dir, output.name, desc, this.manifest.version.to_string (), this.manifest.name, this.manifest.name);
if (this.manifest.dependencies.size > 0) {
pc += "Requires:";
foreach (var lib in this.manifest.dependencies.entries) {
pc += " " + lib.key;
}
pc += "\n";
}
FileUtils.set_contents (Path.build_filename (dir, output.build_dir, output.pc_file), pc);
Logger.logc (LogColor.GREEN, """To install your library:
- Copy %s and %s to %s ;
- Copy %s to %s ;
- Copy %s to %s ;
- Copy %s to %s ;
And then you can run valac with `--pkg %s` !""", output.vapi_file, output.deps_file, Environment.get_variable ("POULP_VAPI_DIR"),
output.header_file, Path.build_filename (Environment.get_variable ("POULP_HEADERS_DIR"), this.manifest.name),
output.pc_file, Environment.get_variable ("POULP_PKG_DIR"),
output.lib_file, Environment.get_variable ("POULP_LIB_DIR"),
output.name);
}
} catch (Error err) {
Logger.log_error (err.message);
output.success = false;
}
return output;
}
/**
* Builds an application
* @param dir The directory where the project is located (to know where to put output files)
* @param release true if we are building a release version
*/
private async BuildOutput build_app (string dir, bool release) {
var valac_args = new ArrayList<string>.wrap ({ "valac" });
AppBuildOutput output = new AppBuildOutput ();
output.project_path = dir;
output.build_dir = "build";
// gettin the output file
string name;
if (this.manifest.output != null) { // if the output is specified in the manifest, we use it
name = this.manifest.output;
} else {
name = this.manifest.name; // else, we use build/name
}
output.set_output (name);
// we add some flags and extensions that are platform specifics
#if WIN32
valac_args.add ("-D WIN32");
#elif BSD
valac_args.add ("-D BSD");
#elif DARWIN
valac_args.add ("-D DARWIN");
#endif
// we create the output directory if it doesn't exist
File out_file = File.new_for_path (name);
string out_parent = out_file.get_parent ().get_path ();
if (FileUtils.test (out_parent, FileTest.EXISTS) == false) {
DirUtils.create_with_parents (out_parent, 0755);
}
valac_args.add ("--output");
valac_args.add (Path.build_filename (output.project_path, output.build_dir, output.bin_file));
// add files, vapidir, and --pkg to valac arguments
if (this.manifest.vapi_dir != null) {
valac_args.add ("--vapidir");
valac_args.add (this.manifest.vapi_dir);
}
if (this.manifest.files.size == 0) {
valac_args.add ("*.vala");
} else {
foreach (string file in this.manifest.files) {
valac_args.add (file);
}
}
foreach (var lib in this.manifest.dependencies.entries) {
valac_args.add (@"--pkg $(lib.key)");
}
if (release) {
foreach (var dev_lib in this.manifest.dev_dependencies.entries) {
valac_args.add (@"--pkg $(dev_lib.key)");
}
}
this.add_features_flags (valac_args);
foreach (string param in this.manifest.compiler_params) {
valac_args.add (param);
}
// run valac
string valac_cmd = string.joinv (" ", valac_args.to_array ());
Logger.logc (LogColor.GREEN, "Running `%s`\n", valac_cmd);
int valac_status = yield spawn_async (valac_cmd, dir);
if (valac_status == 0) {
Logger.logc (LogColor.BOLD_GREEN, "Successfully built project `%s`.", this.manifest.name);
string relative_output_path = File.new_for_commandline_arg (".").
get_relative_path (File.new_for_path (Path.build_filename (dir, output.build_dir, output.bin_file)));
Logger.logc (
LogColor.BOLD_BLUE, "Run %s./%s%s to launch your project.",
LogColor.BOLD_YELLOW, relative_output_path, LogColor.BOLD_BLUE
);
}
return output;
}
private void add_features_flags (ArrayList<string> args) {
foreach (var f in this.manifest.features.entries) {
switch (f.key) { // I would like to have macros in Vala...
case "fast-vapi":
if (f.value) {
args.add ("--use-fast-vapi");
}
break;
case "vapi-comments":
if (f.value) {
args.add ("--vapi-comments");
}
break;
case "debug":
if (f.value) {
args.add ("-g");
args.add ("--save-temps");
}
break;
case "thread":
if (f.value) {
args.add ("--thread");
}
break;
case "memory-profiler":
if (f.value) {
args.add ("--enable-mem-profiler");
}
break;
case "standard-packages":
if (!f.value) {
args.add ("--nostdpkg");
}
break;
case "assert":
if (!f.value) {
args.add ("--disable-assert");
}
break;
case "checking":
if (f.value) {
args.add ("--enable-checking");
}
break;
case "deprecated":
if (f.value) {
args.add ("--enable-deprecated");
}
break;
case "hide-internal":
if (f.value) {
args.add ("--hide-internal");
}
break;
case "experimental":
if (f.value) {
args.add ("--enable-experimental");
}
break;
case "warnings":
if (!f.value) {
args.add ("--disable-warnings");
}
break;
case "fatal-warnings":
if (f.value) {
args.add ("--fatal-warnings");
}
break;
case "since-check":
if (!f.value) {
args.add ("--disable-since-check");
}
break;
case "non-null":
if (f.value) {
args.add ("--enable-experimental-non-null");
}
break;
case "gobject-tracing":
if (f.value) {
args.add ("--enable-gobject-tracing");
}
break;
case "version-header":
if (f.value) {
args.add ("--enable-version-header");
}
break;
case "c-warnings":
if (!f.value) {
args.add ("-X -w");
}
break;
default:
Logger.log_warning ("Unknown feature: %s", f.key);
break;
}
}
}
}
}
namespace Poulp {
/**
* A command that can be run by poulp
*/
public interface Command : Object {
/**
* Called when the command is ran by the user.
*
* @param args Extra arguments passed to the command
*/
public abstract async bool run (string[] args);
/**
* Gives an help message about this command.
*
* @return The help message.
*/
public abstract string help (bool is_short);
}
}
using Gee;
using SemVer;
namespace Poulp {
/**
* Installs packages
*/
public class Installer : Object {
/**
* A list of repositories where we search packages
*/
public ArrayList<Repository> repositories { get; set; default = new ArrayList<Repository> (); }
/**
* Emitted when a package isn't found in any remote repository
*/
public signal void not_found (string pkg);
/**
* Installs a package
*
* @param pkg The package to install
* @param range A {@link SemVer.Range} that should contain a version of the package
*/
public async bool install (string pkg, Range range) {
foreach (var repo in this.repositories) {
if (repo.packages.has_key (pkg)) {
try {
print ("Cloning %s...\n", pkg);
string clone_dir = Path.build_filename (Environment.get_tmp_dir (), "poulp", pkg);
File dir = File.new_for_path (clone_dir);
// removing files if they are already here
if (dir.query_exists ()) {
foreach (string file in recursive_ls (dir.get_path (), true, true)) {
File child = File.new_for_path (file);
child.delete ();
}
} else {
dir.make_directory_with_parents ();
}
int exit_code = yield spawn_async ("git clone %s %s -q".printf (repo.packages[pkg], clone_dir));
if (exit_code != 0) {
Logger.log_error ("Can't clone %s", pkg);
return false;
}
print ("Building %s\n", pkg);
string tags = "";
Process.spawn_sync (clone_dir, { "git", "tag" }, null, SpawnFlags.SEARCH_PATH, null, out tags);
SemVer.Version ver = new SemVer.Version (0, 0, 0);
foreach (string tag in tags.split ("\n")) {
if (valid (tag)) {
var v = new SemVer.Version.parse (tag);
if (v in range && v.gt (ver)) {
ver = v;
}
}
}
if (ver.eq (new SemVer.Version (0, 0, 0))) {
Logger.log_error ("No matching version of %s found.", pkg);
return false;
}
yield spawn_async (@"git checkout $ver -q", clone_dir);
Builder bld = new Builder (new Manifest.for_path (Path.build_filename (clone_dir, "poulp.toml")));
BuildOutput build = yield bld.build (clone_dir);
yield build.install ();
} catch (Error err) {
Logger.log_error (err.message);
}
}
}
return true;
}
}
}
using Gee;
namespace Poulp {
/**
* The result of a library build
*/
public class LibBuildOutput : BuildOutput {
/**
* The (base)name of the .vapi file