Skip to content

Commit

Permalink
Refactor -deps and -makedeps code into its own module (#16686)
Browse files Browse the repository at this point in the history
* Refactor -deps and -makedeps code into its own module

* Destructure Params more in dmd.deps
  • Loading branch information
dkorpel authored Jul 13, 2024
1 parent 845adf8 commit 984317d
Show file tree
Hide file tree
Showing 8 changed files with 294 additions and 227 deletions.
2 changes: 1 addition & 1 deletion compiler/src/build.d
Original file line number Diff line number Diff line change
Expand Up @@ -1566,7 +1566,7 @@ auto sourceFiles()
access.d aggregate.d aliasthis.d argtypes_x86.d argtypes_sysv_x64.d argtypes_aarch64.d arrayop.d
arraytypes.d astenums.d ast_node.d astcodegen.d asttypename.d attrib.d attribsem.d basicmangle.d blockexit.d builtin.d canthrow.d chkformat.d
cli.d clone.d compiler.d cond.d constfold.d cppmangle.d cppmanglewin.d cpreprocess.d ctfeexpr.d
ctorflow.d dcast.d dclass.d declaration.d delegatize.d denum.d dimport.d
ctorflow.d dcast.d dclass.d declaration.d delegatize.d denum.d deps.d dimport.d
dinterpret.d dmacro.d dmangle.d dmodule.d doc.d dscope.d dstruct.d dsymbol.d dsymbolsem.d
dtemplate.d dtoh.d dversion.d enumsem.d escape.d expression.d expressionsem.d func.d funcsem.d hdrgen.d iasm.d iasmgcc.d
impcnvtab.d imphint.d importc.d init.d initsem.d inline.d inlinecost.d intrange.d json.d lambdacomp.d
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dmd/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ Note that these groups have no strict meaning, the category assignments are a bi
| [errorsink.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/errorsink.d) | Error reporting interface |
| [target.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/target.d) | Manage target-specific parameters for cross-compiling (for LDC/GDC) |
| [compiler.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/compiler.d) | Describe a back-end compiler and implements compiler-specific actions |
| [deps.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/deps.d) | Implement the `-deps` and `-makedeps` switches |

### Lexing / parsing

Expand Down
270 changes: 270 additions & 0 deletions compiler/src/dmd/deps.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
/**
* Implement the `-deps` and `-makedeps` switches, which output dependencies of modules for build tools.
*
* The grammar of the `-deps` output is:
* ---
* ImportDeclaration
* ::= BasicImportDeclaration [ " : " ImportBindList ] [ " -> "
* ModuleAliasIdentifier ] "\n"
*
* BasicImportDeclaration
* ::= ModuleFullyQualifiedName " (" FilePath ") : " Protection|"string"
* " [ " static" ] : " ModuleFullyQualifiedName " (" FilePath ")"
*
* FilePath
* - any string with '(', ')' and '\' escaped with the '\' character
* ---
*
* Make dependencies as generated by `-makedeps` look like this:
* ---
* source/app.d:
* source/importa.d \
* source/importb.d
* ---
*
* Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved
* License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
* Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/deps.d, makedeps.d)
* Documentation: https://dlang.org/phobos/dmd_deps.html
* Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/deps.d
*/
module dmd.deps;

import core.stdc.stdio : printf;
import core.stdc.string : strcmp;
import dmd.common.outbuffer;
import dmd.dimport : Import;
import dmd.dmodule : Module;
import dmd.globals : Param, Output;
import dmd.hdrgen : visibilityToBuffer;
import dmd.id : Id;
import dmd.location : Loc;
import dmd.root.filename;
import dmd.root.string : toDString;
import dmd.utils : escapePath;

/**
* Output the makefile dependencies for the -makedeps switch
*
* Params:
* buf = outbuffer to write into
* params = dmd params
* link = an executable is being generated
* lib = a library is being generated
* libExt = file extension of libraries for current target
*/
void writeMakeDeps(ref OutBuffer buf, const ref Param params, bool link, bool lib, const(char)[] libExt) pure
{
// start by resolving and writing the target (which is sometimes resolved during link phase)
if (link && params.exefile)
{
buf.writeEscapedMakePath(&params.exefile[0]);
}
else if (lib)
{
const(char)[] libname = params.libname ? params.libname : FileName.name(params.objfiles[0].toDString);
libname = FileName.forceExt(libname, libExt);

buf.writeEscapedMakePath(&libname[0]);
}
else if (params.objname)
{
buf.writeEscapedMakePath(&params.objname[0]);
}
else if (params.objfiles.length)
{
buf.writeEscapedMakePath(params.objfiles[0]);
foreach (of; params.objfiles[1 .. $])
{
buf.writestring(" ");
buf.writeEscapedMakePath(of);
}
}
else
{
assert(false, "cannot resolve makedeps target");
}

buf.writestring(":");

// then output every dependency
foreach (dep; params.makeDeps.files)
{
buf.writestringln(" \\");
buf.writestring(" ");
buf.writeEscapedMakePath(dep);
}
buf.writenl();
}

/**
* Add an import expression to module dependencies
* Params:
* moduleDeps = output settings for `-deps`
* makeDeps = output settings for `-makedeps`
* fileNameZ = 0-termminated string containing the import expression's resolved filename
* importString = raw string passed to import exp
* imod = module import exp is in
*/
void addImportExpDep(ref Output moduleDeps, ref Output makeDeps, const(char)[] fileNameZ, const(char)[] importString, Module imod)
{
if (moduleDeps.buffer !is null)
{
OutBuffer* ob = moduleDeps.buffer;

if (!moduleDeps.name)
ob.writestring("depsFile ");
ob.writestring(imod.toPrettyChars());
ob.writestring(" (");
escapePath(ob, imod.srcfile.toChars());
ob.writestring(") : ");
if (moduleDeps.name)
ob.writestring("string : ");
ob.write(importString);
ob.writestring(" (");
escapePath(ob, fileNameZ.ptr);
ob.writestring(")");
ob.writenl();
}
if (makeDeps.doOutput)
{
makeDeps.files.push(fileNameZ.ptr);
}
}

/**
* Add an import statement to module dependencies
* Params:
* moduleDeps = output settings
* imp = import to add
* imod = module that the import is in
*/
void addImportDep(ref Output moduleDeps, Import imp, Module imod)
{
// object self-imports itself, so skip that
// https://issues.dlang.org/show_bug.cgi?id=7547
// don't list pseudo modules __entrypoint.d, __main.d
// https://issues.dlang.org/show_bug.cgi?id=11117
// https://issues.dlang.org/show_bug.cgi?id=11164
if (moduleDeps.buffer is null || (imp.id == Id.object && imod.ident == Id.object) ||
strcmp(imod.ident.toChars(), "__main") == 0)
return;

OutBuffer* ob = moduleDeps.buffer;
if (!moduleDeps.name)
ob.writestring("depsImport ");
ob.writestring(imod.toPrettyChars());
ob.writestring(" (");
escapePath(ob, imod.srcfile.toChars());
ob.writestring(") : ");
// use visibility instead of sc.visibility because it couldn't be
// resolved yet, see the comment above
visibilityToBuffer(*ob, imp.visibility);
ob.writeByte(' ');
if (imp.isstatic)
{
ob.writestring("static ");
}
ob.writestring(": ");
foreach (pid; imp.packages)
{
ob.printf("%s.", pid.toChars());
}
ob.writestring(imp.id.toString());
ob.writestring(" (");
if (imp.mod)
escapePath(ob, imp.mod.srcfile.toChars());
else
ob.writestring("???");
ob.writeByte(')');
foreach (i, name; imp.names)
{
if (i == 0)
ob.writeByte(':');
else
ob.writeByte(',');
auto _alias = imp.aliases[i];
if (!_alias)
{
ob.printf("%s", name.toChars());
_alias = name;
}
else
ob.printf("%s=%s", _alias.toChars(), name.toChars());
}
if (imp.aliasId)
ob.printf(" -> %s", imp.aliasId.toChars());
ob.writenl();
}

/**
* Takes a path, and make it compatible with GNU Makefile format.
*
* GNU make uses a weird quoting scheme for white space.
* A space or tab preceded by 2N+1 backslashes represents N backslashes followed by space;
* a space or tab preceded by 2N backslashes represents N backslashes at the end of a file name;
* and backslashes in other contexts should not be doubled.
*
* Params:
* buf = Buffer to write the escaped path to
* fname = Path to escape
*/
void writeEscapedMakePath(ref OutBuffer buf, const(char)* fname) pure
{
uint slashes;

while (*fname)
{
switch (*fname)
{
case '\\':
slashes++;
break;
case '$':
buf.writeByte('$');
goto default;
case ' ':
case '\t':
while (slashes--)
buf.writeByte('\\');
goto case;
case '#':
buf.writeByte('\\');
goto default;
case ':':
// ':' not escaped on Windows because it can
// create problems with absolute paths (e.g. C:\Project)
version (Windows) {}
else
{
buf.writeByte('\\');
}
goto default;
default:
slashes = 0;
break;
}

buf.writeByte(*fname);
fname++;
}
}

///
unittest
{
version (Windows)
{
enum input = `C:\My Project\file#4$.ext`;
enum expected = `C:\My\ Project\file\#4$$.ext`;
}
else
{
enum input = `/foo\bar/weird$.:name#\ with spaces.ext`;
enum expected = `/foo\bar/weird$$.\:name\#\\\ with\ spaces.ext`;
}

OutBuffer buf;
buf.writeEscapedMakePath(input);
assert(buf[] == expected);
}
71 changes: 2 additions & 69 deletions compiler/src/dmd/dsymbolsem.d
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import dmd.dcast;
import dmd.dclass;
import dmd.declaration;
import dmd.denum;
import dmd.deps;
import dmd.dimport;
import dmd.dinterpret;
import dmd.dmodule;
Expand Down Expand Up @@ -1635,75 +1636,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
}

imp.semanticRun = PASS.semanticdone;

// object self-imports itself, so skip that
// https://issues.dlang.org/show_bug.cgi?id=7547
// don't list pseudo modules __entrypoint.d, __main.d
// https://issues.dlang.org/show_bug.cgi?id=11117
// https://issues.dlang.org/show_bug.cgi?id=11164
if (global.params.moduleDeps.buffer is null || (imp.id == Id.object && sc._module.ident == Id.object) ||
strcmp(sc._module.ident.toChars(), "__main") == 0)
return;

/* The grammar of the file is:
* ImportDeclaration
* ::= BasicImportDeclaration [ " : " ImportBindList ] [ " -> "
* ModuleAliasIdentifier ] "\n"
*
* BasicImportDeclaration
* ::= ModuleFullyQualifiedName " (" FilePath ") : " Protection|"string"
* " [ " static" ] : " ModuleFullyQualifiedName " (" FilePath ")"
*
* FilePath
* - any string with '(', ')' and '\' escaped with the '\' character
*/
OutBuffer* ob = global.params.moduleDeps.buffer;
Module imod = sc._module;
if (!global.params.moduleDeps.name)
ob.writestring("depsImport ");
ob.writestring(imod.toPrettyChars());
ob.writestring(" (");
escapePath(ob, imod.srcfile.toChars());
ob.writestring(") : ");
// use visibility instead of sc.visibility because it couldn't be
// resolved yet, see the comment above
visibilityToBuffer(*ob, imp.visibility);
ob.writeByte(' ');
if (imp.isstatic)
{
stcToBuffer(*ob, STC.static_);
ob.writeByte(' ');
}
ob.writestring(": ");
foreach (pid; imp.packages)
{
ob.printf("%s.", pid.toChars());
}
ob.writestring(imp.id.toString());
ob.writestring(" (");
if (imp.mod)
escapePath(ob, imp.mod.srcfile.toChars());
else
ob.writestring("???");
ob.writeByte(')');
foreach (i, name; imp.names)
{
if (i == 0)
ob.writeByte(':');
else
ob.writeByte(',');
Identifier _alias = imp.aliases[i];
if (!_alias)
{
ob.printf("%s", name.toChars());
_alias = name;
}
else
ob.printf("%s=%s", _alias.toChars(), name.toChars());
}
if (imp.aliasId)
ob.printf(" -> %s", imp.aliasId.toChars());
ob.writenl();
addImportDep(global.params.moduleDeps, imp, sc._module);
}

void attribSemantic(AttribDeclaration ad)
Expand Down
Loading

0 comments on commit 984317d

Please sign in to comment.