Skip to content

NGCC is non-hermetic and produces different output depending on source location in file system #39293

Closed

Description

@matthewjh

🐞 bug report

Affected Package

angular/compiler-cli

Description

NGCC produces different output - specifically, in sourcemap transformation - depending on source location in file system.

I have two copies of the same project checked out in two places:

D:/myproj
D:/myproj-other/myproj

If I compare node_modules/@angular/animation between the two (this is just an example), the "sources" fields on the source maps in node_modules/@angular/animations/fesm2015 (again, just an example) are different

D:/myproj-other/myproj/node_modules/@angular/animations/fesm2015/animations.js.map

{"version":3,"file":"animations.js","sources":["../../../../../../packages/animations/src/animation_builder.ts","../../../../../../packages/animations/src/animation_metadata.ts","../../../../../../packages/animations/src/util.ts","../../../../../../packages/animations/src/players/animation_player.ts","../../../../../../packages/animations/src/players/animation_group_player.ts","../../../../../../packages/animations/src/private_export.ts","../../../../../../packages/animations/src/animations.ts","../../../../../../packages/animations/public_api.ts","../../../../../../packages/animations/index.ts","../../../../../../packages/animations/animations.ts"],"names":
...

D:/myproj/node_modules/@angular/animations/fesm2015/animations.js.map

{"version":3,"file":"animations.js","sources":["../../../../../packages/animations/src/animation_builder.ts","../../../../../packages/animations/src/animation_metadata.ts","../../../../../packages/animations/src/util.ts","../../../../../packages/animations/src/players/animation_player.ts","../../../../../packages/animations/src/players/animation_group_player.ts","../../../../../packages/animations/src/private_export.ts","../../../../../packages/animations/src/animations.ts","../../../../../packages/animations/public_api.ts","../../../../../packages/animations/index.ts","../../../../../packages/animations/animations.ts"],"names":[
...

Note that the number of "../" is different between them.

the __ivy_ngcc_bak file shows the original:

{"version":3,"file":"animations.js","sources":["../../../../../../packages/animations/src/animation_builder.ts","../../../../../../packages/animations/src/animation_metadata.ts","../../../../../../packages/animations/src/util.ts","../../../../../../packages/animations/src/players/animation_player.ts","../../../../../../packages/animations/src/players/animation_group_player.ts","../../../../../../packages/animations/src/private_export.ts","../../../../../../packages/animations/src/animations.ts","../../../../../../packages/animations/public_api.ts","../../../../../../packages/animations/index.ts","../../../../../../packages/animations/animations.ts"],
...

Unfortunately, this difference is significant if you want to do any sort of build caching between machines as the hashes of the input files of any dependencies on ngcc-processed node_modules are different. This has caused me a lot of pain as I am trying to set up Bazel in our system. I had problems with cache misses between our build servers and developer machines that appears to be caused by this. As a temporary workaround I have had to check in our node_modules, along with ngcc generated files.

I don't have a firm grasp on the details of this bug, but it appears to me arise from the following code in:

node_modules/@angular/compiler-cli/src/ngtsc/sourcemaps/src/source_file.js

var sourceMap = {
                version: 3,
                file: this.fs.relative(sourcePathDir, this.sourcePath),
                sources: sources.map(function (sf) { return _this.fs.relative(sourcePathDir, sf.sourcePath); }),
                names: names,
                mappings: sourcemap_codec_1.encode(mappings),
                sourcesContent: sources.map(function (sf) { return sf.contents; }),
            };

The line

                sources: sources.map(function (sf) { return _this.fs.relative(sourcePathDir, sf.sourcePath); }),

ends up forming a relative path from

D:/myproj/path/to/file

to
D:/packages/path/to/file

Obviously this is going to be different if the first path is

D:/my/nested/checkout/myproj/path/to/file

🌍 Your Environment

Angular Version:
10.1.5

Anything else relevant?
Windows

Activity

  1. petebacondarwin commented on Oct 15, 2020

    @petebacondarwin
    Contributor

    Thanks for this detailed report @matthewjh - I will take a look at it tomorrow.

  2. matthewjh commented on Oct 15, 2020

    @matthewjh
    ContributorAuthor

    Thank you, @petebacondarwin :)

  3. petebacondarwin commented on Oct 16, 2020

    @petebacondarwin
    Contributor

    So as far as I can tell, this is kind of an issue with the source-maps generated by the Angular project in the first place.

    Since the file paths of original source files are pointing way way up the tree, (e.g. ../../../../../../packages/...) then if the location of the source-map itself is not deep enough in the tree then of course it will produce different relative paths.

    This is equivalent to doing:

    cd /a/b/c/d/e
    cd ../../../x
    

    This should put you in /a/b/x right?

    But if you did:

    cd /a/b
    cd ../../../x
    

    Then you end up in /x

    I appreciate that is not very helpful. So I will take a look into whether we can make ngcc's source-map handling more resilient to this...

  4. petebacondarwin commented on Oct 16, 2020

    @petebacondarwin
    Contributor

    So I guess, if the original source path points so far up the tree that we hit the root, then we should assume that this file never exists, and so just leave the path alone rather than modifying it to be relative to the current source-map. What do you think?

  5. matthewjh commented on Oct 16, 2020

    @matthewjh
    ContributorAuthor

    Does that mean that if I checkout the source code at a sufficiently deep path that running ngcc on these files does not hit the root, I may end up with different source files?

    I'm assuming by root you mean the root of the file system tree, i.e.
    C:/
    or
    D:/

    Is there a way to invoke that logic (fallback to the original) more generally in the event that the path "breaks out" of the current workspace/project subtree? So in D:/my-path/my-proj/node_modules/thing, ../../../ would break out of my-proj and the source map path would left alone. But in D:/my-path/my-proj/node_modules/thing/a/b/c, ../../../ would be fine.

  6. petebacondarwin commented on Oct 16, 2020

    @petebacondarwin
    Contributor

    So, yes I did mean C:/ etc when I said root.

    I think we have a problem here generally, in that even if we identified files that broke out of the root, and "fixed" the paths to them. If you did move to a deeper path, then this logic might not kick in and then we would be left with different paths again.
    (I think this might have been what you are saying).

    Perhaps a better approach is to identify when we have been given the source content inline (as in this case) and then just always modify the file path to something consistent (e.g. just the basename, stripping off any leading path segments).

  7. petebacondarwin commented on Oct 16, 2020

    @petebacondarwin
    Contributor

    I created a possible fix.... #39302

  8. added this to the needsTriage milestone on Oct 16, 2020
  9. added
    P3An issue that is relevant to core functions, but does not impede progress. Important, but not urgent
    on Oct 17, 2020
  10. modified the milestones: needsTriage, Backlog on Oct 17, 2020
  11. petebacondarwin commented on Oct 19, 2020

    @petebacondarwin
    Contributor

    Tracking the underlying issue for this in #14021.
    I think there is still room for ngcc (and our source-map merging tooling) to be more resilient to these dodgy source paths anyway. So I will keep working on this in #39302

  12. matthewjh commented on Oct 19, 2020

    @matthewjh
    ContributorAuthor

    Thanks, @petebacondarwin, for looking at this so quickly. For what it's worth I have tested your fix with our codebase, and there are no longer differences between machines / checkout locations, meaning build caching works perfectly. 👍

  13. angular-automatic-lock-bot commented on Nov 19, 2020

    @angular-automatic-lock-bot

    This issue has been automatically locked due to inactivity.
    Please file a new issue if you are encountering a similar or related problem.

    Read more about our automatic conversation locking policy.

    This action has been performed automatically by a bot.

  14. locked and limited conversation to collaborators on Nov 19, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3An issue that is relevant to core functions, but does not impede progress. Important, but not urgent

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions