From 190bfa0449831a9ddf11737dcb9f1b195f078bc1 Mon Sep 17 00:00:00 2001 From: Alexandre Terrasa Date: Tue, 8 May 2018 11:30:08 -0400 Subject: [PATCH] nitpackage: generate and check Makefiles Signed-off-by: Alexandre Terrasa --- share/man/nitpackage.md | 6 ++ src/nitpackage.nit | 221 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 227 insertions(+) diff --git a/share/man/nitpackage.md b/share/man/nitpackage.md index f49bf26..2ec886b 100644 --- a/share/man/nitpackage.md +++ b/share/man/nitpackage.md @@ -54,6 +54,12 @@ Generate package.ini files. ### `--check-ini` Check package.ini files. +### `--gen-makefile` +Generate Makefile files. + +### `--check-makefile` +Check Makefile files. + ### `-f`, `--force` Force update of existing files. diff --git a/src/nitpackage.nit b/src/nitpackage.nit index a2c2666..794c69f 100644 --- a/src/nitpackage.nit +++ b/src/nitpackage.nit @@ -16,6 +16,7 @@ module nitpackage import frontend +import doc::commands::commands_main redef class ToolContext # --expand @@ -30,6 +31,12 @@ redef class ToolContext # --force var opt_force = new OptionBool("Force update of existing files", "-f", "--force") + # --check-makefile + var opt_check_makefile = new OptionBool("Check Makefile files", "--check-makefile") + + # --gen-makefile + var opt_gen_makefile = new OptionBool("Generate Makefile files", "--gen-makefile") + # nitpackage phase var nitpackage_phase: Phase = new NitPackagePhase(self, null) @@ -37,6 +44,7 @@ redef class ToolContext super option_context.add_option(opt_expand, opt_force) option_context.add_option(opt_check_ini, opt_gen_ini) + option_context.add_option(opt_check_makefile, opt_gen_makefile) end end @@ -60,6 +68,12 @@ private class NitPackagePhase continue end + # Check package Makefiles + if toolcontext.opt_check_makefile.value then + mpackage.check_makefile(toolcontext, mainmodule) + continue + end + # Expand packages if toolcontext.opt_expand.value and not mpackage.is_expanded then var path = mpackage.expand @@ -78,6 +92,16 @@ private class NitPackagePhase toolcontext.info("generated INI file `{path}`", 0) end end + + # Create Makefile + if toolcontext.opt_gen_makefile.value then + if not mpackage.has_makefile or toolcontext.opt_force.value then + var path = mpackage.gen_makefile(toolcontext.modelbuilder.model, mainmodule) + if path != null then + toolcontext.info("generated Makefile `{path}`", 0) + end + end + end end end @@ -253,6 +277,94 @@ redef class MPackage ini.save return ini_path end + + # Makefile + + # The path to `self` Makefile + fun makefile_path: nullable String do + var path = package_path + if path == null then return null + if not is_expanded then return null + return path / "Makefile" + end + + # Does `self` have a Makefile? + fun has_makefile: Bool do + var makefile_path = self.makefile_path + if makefile_path == null then return false + return makefile_path.file_exists + end + + private fun check_makefile(toolcontext: ToolContext, mainmodule: MModule) do + var model = toolcontext.modelbuilder.model + var filter = new ModelFilter(accept_example = false, accept_test = false) + var view = new ModelView(model, mainmodule, filter) + + var cmd_bin = new CmdMains(view, mentity = self) + var res_bin = cmd_bin.init_command + if not res_bin isa CmdSuccess then return + + for mmodule in cmd_bin.results.as(not null) do + if not mmodule isa MModule then continue + + if mmodule.makefile_path == null then + toolcontext.warning(location, "missing-makefile", + "Warning: no Makefile for executable module `{mmodule.full_name}`") + end + end + end + + private fun gen_makefile(model: Model, mainmodule: MModule): nullable String do + var filter = new ModelFilter(accept_example = false, accept_test = false) + var view = new ModelView(model, mainmodule, filter) + + var pkg_path = package_path.as(not null) + var makefile_path = makefile_path.as(not null) + + var bins = new Array[String] + var cmd_bin = new CmdMains(view, mentity = self) + var res_bin = cmd_bin.init_command + if res_bin isa CmdSuccess then + for mmodule in cmd_bin.results.as(not null) do + if not mmodule isa MModule then continue + var mmodule_makefile = mmodule.makefile_path + if mmodule_makefile != null and mmodule_makefile != makefile_path then continue + + var file = mmodule.location.file + if file == null then continue + # Remove package path prefix + var bin_path = file.filename + if pkg_path.has_suffix("/") then + bin_path = bin_path.replace(pkg_path, "") + else + bin_path = bin_path.replace("{pkg_path}/", "") + end + bins.add bin_path + end + end + + if bins.is_empty then return null + + var make = new NitMakefile(bins) + make.render.write_to_file(makefile_path) + return makefile_path + end +end + +redef class MModule + private fun makefile_path: nullable String do + var file = location.file + if file == null then return null + + var dir = file.filename.dirname + var makefile = (dir / "Makefile") + if not makefile.file_exists then return null + + for line in makefile.to_path.read_lines do + if line.has_prefix("{name}:") then return makefile + end + return null + end end redef class ConfigTree @@ -287,6 +399,115 @@ redef class ConfigTree end end +# A Makefile for the Nit project +class NitMakefile + + # Nit files to compile + var nit_files: Array[String] + + # List of rules to add in the Makefile + fun rules: Array[MakeRule] do + var rules = new Array[MakeRule] + + var rule_all = new MakeRule("all", is_phony = true) + rules.add rule_all + + for file in nit_files do + var bin = file.basename.strip_extension + + rule_all.deps.add "bin/{bin}" + + var rule = new MakeRule("bin/{bin}") + rule.deps.add "$(shell $(NITLS) -M {file})" + rule.lines.add "mkdir -p bin/" + rule.lines.add "$(NITC) {file} -o bin/{bin}" + rules.add rule + end + + var rule_check = new MakeRule("check", is_phony = true) + rule_check.lines.add "$(NITUNIT) ." + rules.add rule_check + + var rule_doc = new MakeRule("doc", is_phony = true) + rule_doc.lines.add "$(NITDOC) . -o doc/" + rules.add rule_doc + + var rule_clean = new MakeRule("clean", is_phony = true) + if nit_files.not_empty then + rule_clean.lines.add "rm -rf bin/" + end + rule_clean.lines.add "rm -rf doc/" + rules.add rule_clean + + return rules + end + + # Render `self` + fun render: Writable do + var tpl = new Template + tpl.addn """ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License.\n""" + + if nit_files.not_empty then + tpl.addn "NITC ?= nitc" + tpl.addn "NITLS ?= nitls" + end + tpl.addn "NITUNIT ?= nitunit" + tpl.addn "NITDOC ?= nitdoc" + + for rule in rules do + tpl.add "\n{rule.render.write_to_string}" + end + + return tpl + end +end + +# A rule that goes into a Makefile +class MakeRule + + # Rule name + var name: String + + # Is this rule a `.PHONY` one? + var is_phony: Bool = false is optional + + # Rule dependencies + var deps = new Array[String] + + # Rule lines + var lines = new Array[String] + + # Render `self` + fun render: Writable do + var tpl = new Template + if is_phony then + tpl.addn ".PHONY: {name}" + end + tpl.add "{name}:" + if deps.not_empty then + tpl.add " {deps.join(" ")}" + end + tpl.add "\n" + for line in lines do + tpl.addn "\t{line}" + end + return tpl + end +end + # build toolcontext var toolcontext = new ToolContext var tpl = new Template -- 1.7.9.5