+ var mmodule = self.mmodule
+ var dir = file.dirname
+ if dir != "" then dir.mkdir
+ var f
+ f = new FileWriter.open(file)
+ f.write("# GENERATED FILE\n")
+ f.write("# Docunits extracted from comments\n")
+ if mmodule != null then
+ f.write("intrude import {mmodule.name}\n")
+ end
+ f.write("\n")
+ return f
+ end
+
+ # Compile a unit file and return the compiler return code
+ #
+ # Can terminate the program if the compiler is not found
+ private fun compile_unitfile(file: String): Int
+ do
+ var mmodule = self.mmodule
+ var nitc = toolcontext.find_nitc
+ var opts = new Array[String]
+ if mmodule != null then
+ # FIXME playing this way with the include dir is not safe nor robust
+ opts.add "-I {mmodule.filepath.as(not null).dirname}"
+ end
+ var cmd = "{nitc} --ignore-visibility --no-color -q '{file}' {opts.join(" ")} >'{file}.out1' 2>&1 </dev/null -o '{file}.bin'"
+ var res = toolcontext.safe_exec(cmd)
+ return res
+ end
+
+ # Compile a unit file and return the compiler return code
+ #
+ # Can terminate the program if the compiler is not found
+ private fun compile_single_docunits(dus: Array[DocUnit]): Int
+ do
+ # Generate all unitfiles
+ var files = new Array[String]
+ for du in dus do
+ files.add generate_single_docunit(du)
+ end
+
+ if files.is_empty then return 0
+
+ toolcontext.info("Compile {dus.length} single(s) doc-unit(s) at once", 1)
+
+ # Mass compile them
+ var nitc = toolcontext.find_nitc
+ var opts = new Array[String]
+ if mmodule != null then
+ # FIXME playing this way with the include dir is not safe nor robust
+ opts.add "-I {mmodule.filepath.dirname}"
+ end
+ var cmd = "{nitc} --ignore-visibility --no-color -q '{files.join("' '")}' {opts.join(" ")} > '{prefix}.out1' 2>&1 </dev/null --dir {prefix.dirname}"
+ var res = toolcontext.safe_exec(cmd)
+ if res != 0 then
+ # Mass compilation failure
+ return res
+ end
+
+ # Rename each file into it expected binary name
+ for du in dus do
+ var f = du.test_file.as(not null)
+ toolcontext.safe_exec("mv '{f.strip_extension(".nit")}' '{f}.bin'")
+ du.is_compiled = true
+ end
+
+ return res
+ end
+end
+
+private class NitunitMdVisitor
+ super MdVisitor
+
+ var executor: NitUnitExecutor
+
+ redef fun visit(node) do node.accept_nitunit(self)
+
+ fun parse_code(block: MdCodeBlock) do
+ var code = block.literal
+ if code == null then return
+
+ var meta = block.info or else "nit"
+ # Do not try to test non-nit code.
+ if meta != "nit" then return
+
+ # Try to parse code blocks
+ var executor = self.executor
+ var ast = executor.toolcontext.parse_something(code)
+
+ var mdoc = executor.mdoc
+ assert mdoc != null
+
+ # Skip pure comments
+ if ast isa TComment then return
+
+ # The location is computed according to the starts of the mdoc and the block
+ # Note, the following assumes that all the comments of the mdoc are correctly aligned.
+ var loc = block.location
+ var line_offset = loc.line_start + mdoc.location.line_start - 2
+ var column_offset = loc.column_start + mdoc.location.column_start
+ # Hack to handle precise location in blocks
+ # TODO remove when markdown is more reliable
+ if block isa MdFencedCodeBlock then
+ # Skip the starting fence
+ line_offset += 1
+ else
+ # Account a standard 4 space indentation
+ column_offset += 4
+ end
+
+ # We want executable code
+ if not (ast isa AModule or ast isa ABlockExpr or ast isa AExpr) then
+ var message
+ var l = ast.location
+ # Get real location of the node (or error)
+ var location = new Location(mdoc.location.file,
+ l.line_start + line_offset,
+ l.line_end + line_offset,
+ l.column_start + column_offset,
+ l.column_end + column_offset)
+ if ast isa AError then
+ message = ast.message
+ else
+ message = "Error: Invalid Nit code."
+ end
+
+ var du = new_docunit
+ du.block += code
+ du.error_location = location
+ du.error = message
+ executor.toolcontext.modelbuilder.failed_entities += 1