-
Notifications
You must be signed in to change notification settings - Fork 795
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
Raise error when two assets produce same path #530
Conversation
When trying to generate assets we can have multiple files that generate the same end path. For example `.css` and `.css.erb` and `.scss` all generate a file with `.css` extension. When linking assets (telling sprockets to generate a new file) we can check for the condition that the same asset logical path has already been generated when that happens we should get an error like this: ``` Sprockets::DoubleLinkError: Multiple files with the same output path cannot be linked ("stylesheets/a.css") In "/Users/rschneeman/Documents/projects/sprockets/test/fixtures/double/link_directory_manifest.js" these files were linked: - /Users/rschneeman/Documents/projects/sprockets/test/fixtures/double/stylesheets/a.css - /Users/rschneeman/Documents/projects/sprockets/test/fixtures/double/stylesheets/a.css.erb ``` This does not handle the case where individual assets are directly linked (versus using link_directory).
Failure on windows:
No clue why it only would happen on this method |
Turns out this isn't a windows bug, it's a threading bug. It just happens to show up more often on windows: Here's what's happening. There is some code that calls
You'll notice that there's a yield. What block is being executed? It's this one: sprockets/lib/sprockets/manifest.rb Lines 168 to 196 in d31aabc
You'll notice this code is building promises and then waiting for them: # ...
if promise.nil?
promise = Concurrent::Promise.new(executor: executor) { exporter.call }
concurrent_exporters << promise.execute
else
concurrent_exporters << promise.then { exporter.call }
end
end
end
# make sure all exporters have finished before returning the main thread
concurrent_exporters.each(&:wait!)
save Unfortunately, the block ends before So what is happening is |
Here's what's happening. There is some code that calls `find_all_linked_assets` which is where our new error is coming from ``` environment.find_all_linked_assets(path) do |asset| yield asset end ``` You'll notice that there's a yield. What block is being executed? It's this one: https://github.com/rails/sprockets/blob/d31aabc023bb58035bb1e2e03be6e0d4e88e1539/lib/sprockets/manifest.rb#L168-L196 You'll notice this code is building promises and then waiting for them: ```ruby # ... if promise.nil? promise = Concurrent::Promise.new(executor: executor) { exporter.call } concurrent_exporters << promise.execute else concurrent_exporters << promise.then { exporter.call } end end end # make sure all exporters have finished before returning the main thread concurrent_exporters.each(&:wait!) save ``` Unfortunately, the block ends before `concurrent_exporters.each(&:wait!)` gets called. So what is happening is `environment.find_all_linked_assets(path) do |asset|` will yield `a.css` which enqueues background threads to write contents to disk. Then it will try to yield `a.css.erb` but before it can it will raise the `DoubleRender` error. When this happens the promises that are getting processed in the background are never `wait!`-ed and the program continues from where the exception was rescued. This happens in our test case will try to clean up, yet files are still open and being written to. The operating system tries to delete the directory but there are open file descriptors so it fails, with this somewhat odd error message.
Revert "Merge pull request #530 from rails/schneems/double-render"
When trying to generate assets we can have multiple files that generate the same end path. For example
.css
and.css.erb
and.scss
all generate a file with.css
extension. When linking assets (telling sprockets to generate a new file) we can check for the condition that the same asset logical path has already been generated when that happens we should get an error like this:This does not handle the case where individual assets are directly linked (versus using link_directory).