Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix issue 16746 - Please output Makefile-style depfiles for ninja and make #6961

Closed
wants to merge 1 commit into from

Conversation

eigenraven
Copy link

@eigenraven eigenraven commented Jul 3, 2017

This adds -M, -Mffile, -Mddirectory, -Mttarget commandline options akin to C(++) compilers, and implements generation of make depfiles which can be used by buildsystems such as Ninja, Make or Meson.

Link to issue: https://issues.dlang.org/show_bug.cgi?id=16746

@dlang-bot
Copy link
Contributor

dlang-bot commented Jul 3, 2017

Thanks for your pull request and interest in making D better, @KubaSz! We are looking forward to reviewing it, and you should be hearing from a maintainer soon.
Please verify that your PR follows this checklist:

  • My PR is fully covered with tests (you can see the annotated coverage diff directly on GitHub with CodeCov's browser extension
  • My PR is as minimal as possible (smaller, focused PRs are easier to review than big ones)
  • I have provided a detailed rationale explaining my changes
  • New or modified functions have Ddoc comments (with Params: and Returns:)

Please see CONTRIBUTING.md for more information.


If you have addressed all reviews or aren't sure how to proceed, don't hesitate to ping us with a simple comment.

Bugzilla references

Auto-close Bugzilla Severity Description
16746 enhancement Please output Makefile-style depfiles for ninja and make

Testing this PR locally

If you don't have a local development environment setup, you can use Digger to test this PR:

dub fetch digger
dub run digger -- build "master + dmd#6961"

@wilzbach
Copy link
Member

wilzbach commented Jul 5, 2017

Seems like gdc doesn't like this change:

.. compilable/makedeps.d          -M -MF=test_results/compilable/makedeps.dep ()
*** glibc detected *** ../src/dmd: double free or corruption (!prev): 0x0000000001fc7280 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x7da66)[0x2b5c8318ca66]
../src/dmd[0x63a5f1]
../src/dmd[0x65c9dd]
../src/dmd[0x660166]
../src/dmd[0x66046e]
../src/dmd[0x64060e]
../src/dmd[0x6376a5]
../src/dmd[0x637719]
../src/dmd[0x63730f]
../src/dmd[0x6374f2]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xed)[0x2b5c831307ed]
../src/dmd[0x403c09]
======= Memory map: ========
00400000-00782000 r-xp 00000000 fc:06 17744159                           /home/travis/build/dlang/dmd/src/dmd
00782000-007d2000 rw-p 00382000 fc:06 17744159                           /home/travis/build/dlang/dmd/src/dmd
007d2000-007df000 rw-p 00000000 00:00 0 
01f41000-01fd6000 rw-p 00000000 00:00 0                                  [heap]
2b5c829d4000-2b5c829f6000 r-xp 00000000 fc:06 109174333                  /lib/x86_64-linux-gnu/ld-2.15.so

@@ -8413,6 +8413,22 @@ extern (C++) final class ImportExp : UnaExp
ob.writestring(")");
ob.writenl();
}
if (global.params.makeDeps !is null)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm CodeCov says this is tested:

image

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added an import expression to the test file, this should cover these lines.

@ibuclaw
Copy link
Member

ibuclaw commented Jul 5, 2017

Seems like gdc doesn't like this change:

FYI, gdc compiled test uses GC allocation, not bump-the-pointer. So this means that there is a genuine logic bug in the implementation.

src/ddmd/mars.d Outdated
depBuffer.writeByte(' ');
}
auto depf = File(outpath);
depf.setbuffer(cast(void*)depBuffer.data, depBuffer.offset);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is normally why there's a double free. File is freeing the buffer owned by OutBuffer. You need to tell depf._ref = 1.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. This is my first encounter with dmd's code, so sorry that I didn't notice the _ref field in File.

@@ -199,6 +203,7 @@ struct Global
const char *hdr_ext; // for D 'header' import files
const char *json_ext; // for JSON files
const char *map_ext; // for .map files
const char *makedep_ext; // for make-style dependency files
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All _ext fields should just go. There is no need for them.

src/ddmd/mars.d Outdated
if(global.params.makeDeps !is null)
error(Loc(), "-M/-MD can only be provided once!");
global.params.makeDeps = new Strings();
break;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-MD writes deps to a file implicitly.

@@ -172,6 +172,10 @@ struct Param
const(char)* moduleDepsFile; // filename for deps output
OutBuffer* moduleDeps; // contents to be written to deps file

const(char)* makeDepsFile; // filename for make-style dependencies output
const(char)* makeDepsTarget; // the generated make target name
Array!(const(char)*)* makeDeps; // array of paths that the compiled module(s) depend on
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if these are strictly needed, or we should start requesting that new driver-only fields be added privately in mars.d so as to not affect other backends.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The makeDeps field could be private, but the File and Target can be useful for other drivers to input their commandline options to dmdfe. I've basically followed what changes the -deps option that was already built into dmd introduced, so that's why I added these fields here.

@eigenraven eigenraven force-pushed the makedepends branch 2 times, most recently from 9303ba1 to 5a5ba85 Compare July 5, 2017 10:56
Copy link
Member

@MartinNowak MartinNowak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, interesting PR :).
While dmd's command line is weird, it doesn't become better by adopting switches with different conventions. Please make your additions consistent with existing switches.
Furthermore this apparently cannot generate multiple output deps files, we need sth. that also works with multiple source and output files, while also working with parallel compilation (can't write everything into one file).

src/ddmd/mars.d Outdated
@@ -147,6 +147,10 @@ Where:
-J=<directory> look for string imports also in directory
-L=<linkerflag> pass linkerflag to link
-lib generate library rather than object files
-M generate make-style dependency file, generates no other output files
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dmd already has -o- to turn off object file generation.

src/ddmd/mars.d Outdated
-M generate make-style dependency file, generates no other output files
-MD generate make-style dependency file
-MF=<filename> specifies the dependency output file
-MT=<name> specifies the name of the target rule in the dependencies file
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This differs quite a lot from dmd's other options, look at -H, -Hd, -Hf, -op, -D.
In particular you want to be able to specify an output folder with -Md and obey -op, and use -Mf instead of -MF.
Please make sure to not mixup dependency and deps file in the explanation.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure how -MT could work for multiple object files in one compilation (not a typical make use-case though possible).

src/ddmd/mars.d Outdated
const(char)* mtarget = global.params.makeDepsTarget;
if (!outpath)
{
outpath = FileName.forceExt((*global.params.objfiles)[0], "dep");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're sure there are object files when we run with -o-?
Better generate your own paths.

src/ddmd/mars.d Outdated
global.params.makeDeps = new Strings();
break;
case 'F':
if(p[3] != '=' || p[4] == '\0')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do not use = for any of the path arguments, just -Hffile.di for example.

grep 'makedeps.sh' ${RESULTS_DIR}/compilable/makedeps2.dep || exit 1
grep 'makedeps_a.d' ${RESULTS_DIR}/compilable/makedeps2.dep || exit 1
grep 'object.d' ${RESULTS_DIR}/compilable/makedeps2.dep || exit 1
grep '__entrypoint' ${RESULTS_DIR}/compilable/makedeps2.dep && exit 1
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might want to include colon (:) in your grep searches.

@eigenraven
Copy link
Author

eigenraven commented Aug 12, 2017

Sorry for the long delay in fixing my PR, I had a surgery and needed time to recover. I've pushed the fixes now:

  • Changed the cmdline args to match -D*: -M, -Mf, -Md; -Mttarget follows the convention, they also respect -op by generating paths using the same logic as the rest of the code (Module's setOutfile)
  • Added a test for the target itself with a semicolon in the shell script
  • The code now fully handles multiple input modules:
    • if Mf is specified, outputs all dependencies to a single file
    • if Mf is not specified, generates a new ".dep" file for each input file basing on -Md/-op/cwd
    • if MtTARGET is specified, all the targets are replaced with TARGET: (it's also clang's behaviour)
  • Dependency searching now uses a string hash table instead of the previous O(n^2) search for identical matches
  • Rebased against master

@eigenraven
Copy link
Author

I have rebased the PR against the current master branch.

@ximion
Copy link

ximion commented Sep 29, 2017

Any news on this? It's kind of important to make splitbuilds work out of the box in Automake, Meson & Co.

@eigenraven
Copy link
Author

Would it be possible for you to re-review this PR? @MartinNowak @ibuclaw

@eigenraven
Copy link
Author

Rebased against master.

bool doMakeDeps; // generate make-style dependencies file
const(char)* makeDepsFile; // filename for make-style dependencies output
const(char)* makeDepsDir; // directory for make-style dependencies output
const(char)* makeDepsTarget; // the make target name
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All new public symbols should have Ddoc comments.

bool doMakeDeps; // generate make-style dependencies file
const(char)* makeDepsFile; // filename for make-style dependencies output
const(char)* makeDepsDir; // directory for make-style dependencies output
const(char)* makeDepsTarget; // the make target name
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can these const(char)* fields be made into D strings, i.e. const(char)[]? On the C++ side an ABI compatible struct can be used.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As much as I'd like this to happen, I don't think it's fair to ask a contributor to break the current status quo.
Such a change should be done on its own.

@@ -381,6 +381,7 @@ extern (C++) final class Module : Package
Dsymbols* decldefs; // top level declarations for this Module

Modules aimports; // all imported modules
Strings astringImports; // all string-imported files
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps this can be an array of const(char)[] and an ABI compatible struct on the C++ side.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment about the status quo. I'd like to replace the Array aliases as much as the next guy, but I don't think we should put that kind of weight on this PR.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also think that this is not really in the scope of this PR, and I think a "standard" way to do it (perhaps with predefined structs for most common array typs) should be defined first and a refactor PR could be done with that in place.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then it will never happen and at the same time we’re adding more code that needs to change later 😞

src/dmd/mars.d Outdated
{
auto mpath = m.srcfile.toString();
// Skip modules already in the table, add self
if (moduleImportedPaths.insert(mpath.ptr, mpath.length, null) is null)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add an overload to insert that takes const(char)[] instead of a pointer and length.

@@ -765,6 +765,11 @@ nothrow:
{
return str;
}

extern (D) const(char)[] toString() const pure
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can be made nothrow.

extern (D) const(char)[] toString() pure const
{
return name.toString();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can be made nothrow.

@@ -66,11 +66,16 @@ nothrow:
}
}

extern (C++) const(char)* toChars() pure
extern (C++) const(char)* toChars() pure const
{
return name.toChars();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can be made nothrow, I assume.

src/dmd/mars.d Outdated
{
// no actual file is created, uses just the path
File* objfile = m.setOutfile(global.params.objname, global.params.objdir, m.arg, global.obj_ext);
escapePathSpaces(&mdout, objfile.toChars());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please used the newly added toString method instead.

src/dmd/mars.d Outdated
@@ -869,20 +869,109 @@ private int tryMain(size_t argc, const(char)** argv)

// inlineScan incrementally run semantic3 of each expanded functions.
// So deps file generation should be moved after the inlinig stage.
if (global.params.moduleDeps)
// inlineScan incrementally run semantic3 of each expanded functions.
// So deps file generation should be moved after the inlinig stage.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicated comment

src/dmd/mars.d Outdated
{
const(char)* mpath = m.srcfile.toChars();
// Skip modules already in the table, add self
if(moduleImportedPaths.insert(mpath, strlen(mpath), null) is null)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (

src/dmd/utils.d Outdated
* buf = Buffer to write the escaped path to
* fname = Path to escape
*/
void escapePathSpaces(OutBuffer* buf, const(char)* fname)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any chance to move this to filename ?

bool doMakeDeps; // generate make-style dependencies file
const(char)* makeDepsFile; // filename for make-style dependencies output
const(char)* makeDepsDir; // directory for make-style dependencies output
const(char)* makeDepsTarget; // the make target name
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As much as I'd like this to happen, I don't think it's fair to ask a contributor to break the current status quo.
Such a change should be done on its own.

@@ -381,6 +381,7 @@ extern (C++) final class Module : Package
Dsymbols* decldefs; // top level declarations for this Module

Modules aimports; // all imported modules
Strings astringImports; // all string-imported files
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment about the status quo. I'd like to replace the Array aliases as much as the next guy, but I don't think we should put that kind of weight on this PR.

@atilaneves
Copy link
Contributor

This PR would also close the issue I filed.

On the whole "why makefile dependencies?" front, I'd like to add this: this is required for ninja to work. Reggae only supports ninja by wrapping dmd and offering the necessary functionality.

@WalterBright
Copy link
Member

I thought I'd added a comment here that dmd has the -deps switch which was supposed to emit makefile dependencies. I don't know when it got driven off into a ditch of uselessness.

@eigenraven
Copy link
Author

I think it is too late now to change the behavior of the -deps switch, quite a few tools seem to rely on its format :(


extern (D) const(char)[] toString() const pure nothrow
{
return str[0 .. strlen(str)];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

str can be null

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see #8550, which should be a separate PR anyway.

@JinShil
Copy link
Contributor

JinShil commented Aug 9, 2018

So if this is merged, we'll have -deps, -X/-Xf, and then -M/-Mffile/-Mddirectory/-Mttarget all doing similar things in a different way? Can this herd of cats be reigned in somehow?

@andralex
Copy link
Member

andralex commented Aug 9, 2018

The right design is to output the information in a simple format and leave it to tooling to translate to others. Sadly, one little uninspired idea after another, we're here.

@WalterBright
Copy link
Member

And so we arrive at the nightmarish blizzard of switches I so wanted to avoid :-(

@ximion
Copy link

ximion commented Aug 9, 2018

Well, at some well-announced point in the future, you could in theory clean up the compiler flags and adjust them to something that you like.
Personally, I always prefer when a compiler copies the gcc CLI - not because it's particularly great, but because it is very complete and familiar, so achieving the same result with the same flags on different compilers is easy.
Anything goes, though.
The one thing I really want to avoid is having some extra binary to convert depfiles into multiple formats - that's not performant at all and will slow down compilation a lot due to the sheer number of process spawns.

// So deps file generation should be moved after the inlinig stage.
if (global.params.moduleDeps)
// So deps file generation should be moved after the inlining stage.
if (global.params.moduleDeps || global.params.doMakeDeps)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You probably don't want to reuse -deps functionality as it is no longer useful for make. Analysing code via semantic3OnDependencies also reports local imports that are not used when compiling the code without the -deps option. It can also slow down compilation considerably. See discussion in #6748.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was insightful, thank you. I've moved the semantic3OnDependencies call to be done only for the old -deps switch.

@GoaLitiuM
Copy link
Contributor

What happened to this PR? Can we get this rebased and back to track again? I really wanted to see this go through to improve the building process with ninja.

@MartinNowak
Copy link
Member

It's not in a particularly good shape, adds lots of extra switches with partly redundant functionality (#6961 (comment)).
More importantly recursive import dependencies are not what you want for make et.al.
Those tools know well how to stich together shallow dependencies themselves. Also recursive dependencies can only be reliably generated by the compiler when actually compiling a dependency (e.g. local imports in templates).

@MartinNowak
Copy link
Member

MartinNowak commented Dec 23, 2018

The basic idea to generate makefile compatible output is good though, maybe just add a -makedeps=<filename> switch, or given that -deps was broken since a year (#9122), maybe you could just change the format of -deps=<filename>. Another approach would be to switch the output format based on the file-ending, though makefile dependencies often use .d which might be somewhat confusing.

@Geod24
Copy link
Member

Geod24 commented Dec 29, 2020

Another implementation, #12011, has been merged into master.

@Geod24 Geod24 closed this Dec 29, 2020
@ximion
Copy link

ximion commented Dec 29, 2020

Excellent, many thanks to everyone involved! This helps a lot for project with mixed languages and D with buildsystems using ninja and make :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.