diff --git a/.gitignore b/.gitignore index 7b38a79d16cc..3758cc83bf1a 100644 --- a/.gitignore +++ b/.gitignore @@ -127,6 +127,7 @@ lib/ruby/stdlib/singleton* lib/ruby/stdlib/stringio* lib/ruby/stdlib/strscan* lib/ruby/stdlib/subspawn* +lib/ruby/stdlib/tempfile.rb lib/ruby/stdlib/time.rb lib/ruby/stdlib/timeout* lib/ruby/stdlib/tracer* diff --git a/core/src/main/java/org/jruby/ext/tempfile/Tempfile.java b/core/src/main/java/org/jruby/ext/tempfile/Tempfile.java deleted file mode 100644 index 11fcc1a4e725..000000000000 --- a/core/src/main/java/org/jruby/ext/tempfile/Tempfile.java +++ /dev/null @@ -1,269 +0,0 @@ -/* - ***** BEGIN LICENSE BLOCK ***** - * Version: EPL 2.0/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Eclipse Public - * License Version 2.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of - * the License at http://www.eclipse.org/legal/epl-v20.html - * - * Software distributed under the License is distributed on an "AS - * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or - * implied. See the License for the specific language governing - * rights and limitations under the License. - * - * Copyright (C) 2004-2009 Thomas E Enebo - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the EPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the EPL, the GPL or the LGPL. - ***** END LICENSE BLOCK *****/ - -package org.jruby.ext.tempfile; - -import jnr.constants.platform.Errno; -import jnr.constants.platform.OpenFlags; -import jnr.posix.POSIX; -import org.jruby.Finalizable; -import org.jruby.Ruby; -import org.jruby.RubyClass; -import org.jruby.RubyException; -import org.jruby.RubyFile; -import org.jruby.RubyFileStat; -import org.jruby.RubyFixnum; -import org.jruby.RubyHash; -import org.jruby.RubyString; -import org.jruby.RubySystemCallError; -import org.jruby.anno.JRubyClass; -import org.jruby.anno.JRubyMethod; -import org.jruby.exceptions.RaiseException; -import org.jruby.platform.Platform; -import org.jruby.runtime.*; -import org.jruby.runtime.builtin.IRubyObject; - -import java.io.File; -import java.io.IOException; - -/** - * An implementation of tempfile.rb in Java. - */ -@JRubyClass(name="Tempfile", parent="File") -public class Tempfile extends RubyFile implements Finalizable { - public static RubyClass createTempfileClass(Ruby runtime) { - RubyClass tempfileClass = runtime.defineClass("Tempfile", runtime.getFile(), Tempfile::new); - - tempfileClass.defineAnnotatedMethods(Tempfile.class); - - return tempfileClass; - } - - private File tmpFile = null; - private IRubyObject tmpname; - private IRubyObject opts; - private IRubyObject mode; - - // This should only be called by this and RubyFile. - // It allows this object to be created without a IOHandler. - public Tempfile(Ruby runtime, RubyClass type) { - super(runtime, type); - } - - @JRubyMethod(optional = 4, checkArity = false, visibility = Visibility.PRIVATE, keywords = true) - @Override - public IRubyObject initialize(ThreadContext context, IRubyObject[] args, Block unused) { - Arity.checkArgumentCount(context, args, 0, 4); - - if (args.length == 0) { - args = new IRubyObject[] { RubyString.newEmptyString(context.runtime) }; - } - - IRubyObject Tmpname = context.runtime.getDir().getConstant("Tmpname", false); - Block block = CallBlock19.newCallClosure(this, getMetaClass(), Signature.OPTIONAL, new TempfileCallback(), context); - context.sites.Tempfile.create.call(context, Tmpname, Tmpname, args, block); - // ::Dir::Tmpname.create(basename, tmpdir=nil, **options) { |tmpname, n, opts| ... } - - // GH#1905: don't use JDK's deleteOnExit because it grows a set without bounds - context.runtime.addInternalFinalizer(Tempfile.this); - - return context.nil; - } - - private class TempfileCallback implements BlockCallback { - @Override - public IRubyObject call(ThreadContext context, IRubyObject[] args, Block block) { - Ruby runtime = context.runtime; - IRubyObject tmpname = args[0], opts = args.length > 2 ? args[2] : context.nil; - - // MRI uses CREAT also, but we create the file ourselves before opening it - int mode = OpenFlags.O_RDWR.intValue() /*| OpenFlags.O_CREAT.intValue()*/ | OpenFlags.O_EXCL.intValue(); - IRubyObject perm = runtime.newFixnum(0600); - - // check for trailing options hash and prepare it - if (!opts.isNil()) { - RubyHash options = (RubyHash)opts; - IRubyObject optsMode = options.delete(context, runtime.newSymbol("mode"), Block.NULL_BLOCK); - if (!optsMode.isNil()) { - mode |= optsMode.convertToInteger().getIntValue(); - } - options.op_aset(context, runtime.newSymbol("perm"), perm); - } else { - opts = perm; - } - - try { - // This is our logic for creating a delete-on-exit tmpfile using JDK features - - File tmp = new File(tmpname.convertToString().toString()); - if (tmp.createNewFile()) { - runtime.getPosix().chmod(tmp.getAbsolutePath(), 0600); - tmpFile = tmp; - } else { - throw context.runtime.newErrnoEEXISTError(getPath()); - } - } catch (IOException e) { - throw context.runtime.newIOErrorFromException(e); - } - - // Logic from tempfile.rb starts again here - - // let RubyFile do its init logic to open the channel - Tempfile.super.initialize(context, new IRubyObject[] { tmpname, runtime.newFixnum(mode), opts }, Block.NULL_BLOCK); - Tempfile.this.tmpname = tmpname; - - Tempfile.this.mode = runtime.newFixnum(mode & ~(OpenFlags.O_CREAT.intValue() | OpenFlags.O_EXCL.intValue())); - Tempfile.this.opts = opts; - - return opts; - } - } - - @JRubyMethod - public IRubyObject open(ThreadContext context) { - if (!isClosed()) rbIoClose(context); - - // MRI doesn't do this, but we need to reset to blank slate - openFile = null; - - Tempfile.super.initialize(context, new IRubyObject[]{tmpname, mode, opts}, Block.NULL_BLOCK); - - return this; - } - - @JRubyMethod(visibility = Visibility.PROTECTED) - public IRubyObject _close(ThreadContext context) { - return !isClosed() ? super.close(context) : context.nil; - } - - @JRubyMethod(optional = 1, checkArity = false) - public IRubyObject close(ThreadContext context, IRubyObject[] args, Block block) { - int argc = Arity.checkArgumentCount(context, args, 0, 1); - - boolean unlink = argc == 1 ? args[0].isTrue() : false; - return unlink ? close_bang(context) : _close(context); - } - - @JRubyMethod(name = "close!") - public IRubyObject close_bang(ThreadContext context) { - _close(context); - unlink(context); - return context.nil; - } - - @JRubyMethod(name = {"unlink", "delete"}) - public IRubyObject unlink(ThreadContext context) { - if (openFile.getPath() == null) return context.nil; - - Ruby runtime = context.runtime; - POSIX posix = runtime.getPosix(); - - if (posix.isNative() && !Platform.IS_WINDOWS) { - IRubyObject oldExc = context.runtime.getGlobalVariables().get("$!"); // Save $! - try { - RubyFile.unlink(context, this); - } catch (RaiseException re) { - RubyException excp = re.getException(); - if (!(excp instanceof RubySystemCallError)) throw re; - - int errno = (int)((RubySystemCallError)excp).errno().convertToInteger().getLongValue(); - if (errno != Errno.ENOENT.intValue() && errno != Errno.EACCES.intValue()) { - throw re; - } - context.runtime.getGlobalVariables().set("$!", oldExc); // Restore $! - } - openFile.setPath(null); - tmpname = context.nil; - } else { - // JRUBY-6688: delete when closed, warn otherwise - if (isClosed()) { - // the user intends to delete the file immediately, so do it - if (!tmpFile.exists() || tmpFile.delete()) { - openFile.setPath(null); - tmpname = context.nil; - } - } else { - // else, no-op, since we can't unlink the file without breaking stat et al - context.runtime.getWarnings().warn("Tempfile#unlink or delete called on open file; ignoring"); - } - } - return context.nil; - } - - @JRubyMethod(name = {"size", "length"}) - @Override - public IRubyObject size(ThreadContext context) { - if (!isClosed()) { - flush(context); - RubyFileStat stat = (RubyFileStat)stat(context); - return stat.size(); - } else if (tmpname != null && !tmpname.isNil()) { - RubyFileStat stat = (RubyFileStat)stat(context, getMetaClass(), tmpname); - return stat.size(); - } else { - return RubyFixnum.zero(context.runtime); - } - } - - @Deprecated - public static IRubyObject open19(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) { - return open(context, recv, args, block); - } - - @JRubyMethod(optional = 4, checkArity = false, meta = true, keywords = true) - public static IRubyObject open(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) { - RubyClass klass = (RubyClass) recv; - Tempfile tempfile = (Tempfile) klass.newInstance(context, args, block); - - if (block.isGiven()) { - try { - return block.yield(context, tempfile); - } finally { - if (!tempfile.isClosed()) tempfile.close(); - } - } else { - return tempfile; - } - } - - @JRubyMethod - public RubyString inspect(ThreadContext context) { - return super.inspect(context); - } - - @Override - public void finalize() throws Throwable { - try { - super.finalize(); - } finally { - tmpFile.delete(); - } - } - -} diff --git a/core/src/main/java/org/jruby/ext/tempfile/TempfileLibrary.java b/core/src/main/java/org/jruby/ext/tempfile/TempfileLibrary.java deleted file mode 100644 index 29216d7ad444..000000000000 --- a/core/src/main/java/org/jruby/ext/tempfile/TempfileLibrary.java +++ /dev/null @@ -1,42 +0,0 @@ -/***** BEGIN LICENSE BLOCK ***** - * Version: EPL 2.0/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Eclipse Public - * License Version 2.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of - * the License at http://www.eclipse.org/legal/epl-v20.html - * - * Software distributed under the License is distributed on an "AS - * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or - * implied. See the License for the specific language governing - * rights and limitations under the License. - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the EPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the EPL, the GPL or the LGPL. - ***** END LICENSE BLOCK *****/ - -package org.jruby.ext.tempfile; - -import java.io.IOException; - -import org.jruby.Ruby; -import org.jruby.runtime.load.Library; - -/** - * - * @author enebo - */ -public class TempfileLibrary implements Library { - public void load(Ruby runtime, boolean wrap) throws IOException { - Tempfile.createTempfileClass(runtime); - } -} diff --git a/lib/pom.rb b/lib/pom.rb index 30d3ff1717fb..0cfb95b3e258 100644 --- a/lib/pom.rb +++ b/lib/pom.rb @@ -105,8 +105,7 @@ def log(message=nil) ['ffi-bindings-libfixposix', '0.5.1.0'], # https://github.com/ruby/syslog/issues/1 # ['syslog', '0.1.0'], - # https://github.com/ruby/tempfile/issues/7 - # ['tempfile', '0.1.2'], + ['tempfile', '0.1.3'], ['time', '0.2.2'], ['timeout', '0.3.2'], # https://github.com/ruby/tmpdir/issues/13 diff --git a/lib/pom.xml b/lib/pom.xml index aa1ad825dee3..467ee813c114 100644 --- a/lib/pom.xml +++ b/lib/pom.xml @@ -798,6 +798,19 @@ DO NOT MODIFY - GENERATED CODE + + rubygems + tempfile + 0.1.3 + gem + provided + + + rubygems + jar-dependencies + + + rubygems time @@ -1125,6 +1138,7 @@ DO NOT MODIFY - GENERATED CODE specifications/subspawn-posix-0.1.1* specifications/ffi-binary-libfixposix-0.5.1.1* specifications/ffi-bindings-libfixposix-0.5.1.0* + specifications/tempfile-0.1.3* specifications/time-0.2.2* specifications/timeout-0.3.2* specifications/tsort-0.1.0* @@ -1203,6 +1217,7 @@ DO NOT MODIFY - GENERATED CODE gems/subspawn-posix-0.1.1*/**/* gems/ffi-binary-libfixposix-0.5.1.1*/**/* gems/ffi-bindings-libfixposix-0.5.1.0*/**/* + gems/tempfile-0.1.3*/**/* gems/time-0.2.2*/**/* gems/timeout-0.3.2*/**/* gems/tsort-0.1.0*/**/* @@ -1281,6 +1296,7 @@ DO NOT MODIFY - GENERATED CODE cache/subspawn-posix-0.1.1* cache/ffi-binary-libfixposix-0.5.1.1* cache/ffi-bindings-libfixposix-0.5.1.0* + cache/tempfile-0.1.3* cache/time-0.2.2* cache/timeout-0.3.2* cache/tsort-0.1.0* diff --git a/lib/ruby/stdlib/tempfile.rb b/lib/ruby/stdlib/tempfile.rb deleted file mode 100644 index 4bce7cdc279d..000000000000 --- a/lib/ruby/stdlib/tempfile.rb +++ /dev/null @@ -1,155 +0,0 @@ -# frozen_string_literal: true -# -# tempfile - manipulates temporary files -# -# $Id$ -# - -# require 'delegate' # JRuby: unused -require 'tmpdir' - -# JRuby: load built-in tempfile library -JRuby::Util.load_ext("org.jruby.ext.tempfile.TempfileLibrary") - -# A utility class for managing temporary files. When you create a Tempfile -# object, it will create a temporary file with a unique filename. A Tempfile -# objects behaves just like a File object, and you can perform all the usual -# file operations on it: reading data, writing data, changing its permissions, -# etc. So although this class does not explicitly document all instance methods -# supported by File, you can in fact call any File instance method on a -# Tempfile object. -# -# == Synopsis -# -# require 'tempfile' -# -# file = Tempfile.new('foo') -# file.path # => A unique filename in the OS's temp directory, -# # e.g.: "/tmp/foo.24722.0" -# # This filename contains 'foo' in its basename. -# file.write("hello world") -# file.rewind -# file.read # => "hello world" -# file.close -# file.unlink # deletes the temp file -# -# == Good practices -# -# === Explicit close -# -# When a Tempfile object is garbage collected, or when the Ruby interpreter -# exits, its associated temporary file is automatically deleted. This means -# that's it's unnecessary to explicitly delete a Tempfile after use, though -# it's good practice to do so: not explicitly deleting unused Tempfiles can -# potentially leave behind large amounts of tempfiles on the filesystem -# until they're garbage collected. The existence of these temp files can make -# it harder to determine a new Tempfile filename. -# -# Therefore, one should always call #unlink or close in an ensure block, like -# this: -# -# file = Tempfile.new('foo') -# begin -# # ...do something with file... -# ensure -# file.close -# file.unlink # deletes the temp file -# end -# -# === Unlink after creation -# -# On POSIX systems, it's possible to unlink a file right after creating it, -# and before closing it. This removes the filesystem entry without closing -# the file handle, so it ensures that only the processes that already had -# the file handle open can access the file's contents. It's strongly -# recommended that you do this if you do not want any other processes to -# be able to read from or write to the Tempfile, and you do not need to -# know the Tempfile's filename either. -# -# For example, a practical use case for unlink-after-creation would be this: -# you need a large byte buffer that's too large to comfortably fit in RAM, -# e.g. when you're writing a web server and you want to buffer the client's -# file upload data. -# -# Please refer to #unlink for more information and a code example. -# -# == Minor notes -# -# Tempfile's filename picking method is both thread-safe and inter-process-safe: -# it guarantees that no other threads or processes will pick the same filename. -# -# Tempfile itself however may not be entirely thread-safe. If you access the -# same Tempfile object from multiple threads then you should protect it with a -# mutex. -class Tempfile - # class Remover # :nodoc: - # def initialize(tmpfile) - # @pid = Process.pid - # @tmpfile = tmpfile - # end - # - # def call(*args) - # return if @pid != Process.pid - # - # $stderr.puts "removing #{@tmpfile.path}..." if $DEBUG - # - # @tmpfile.close - # begin - # File.unlink(@tmpfile.path) - # rescue Errno::ENOENT - # end - # - # $stderr.puts "done" if $DEBUG - # end - # end -end - -# Creates a temporary file as usual File object (not Tempfile). -# It doesn't use finalizer and delegation. -# -# If no block is given, this is similar to Tempfile.new except -# creating File instead of Tempfile. -# The created file is not removed automatically. -# You should use File.unlink to remove it. -# -# If a block is given, then a File object will be constructed, -# and the block is invoked with the object as the argument. -# The File object will be automatically closed and -# the temporary file is removed after the block terminates. -# The call returns the value of the block. -# -# In any case, all arguments (+basename+, +tmpdir+, +mode+, and -# **options) will be treated as Tempfile.new. -# -# Tempfile.create('foo', '/home/temp') do |f| -# # ... do something with f ... -# end -# -def Tempfile.create(basename="", tmpdir=nil, mode: 0, **options) - tmpfile = nil - Dir::Tmpname.create(basename, tmpdir, **options) do |tmpname, n, opts| - mode |= File::RDWR|File::CREAT|File::EXCL - opts[:perm] = 0600 - tmpfile = File.open(tmpname, mode, **opts) - end - if block_given? - begin - yield tmpfile - ensure - unless tmpfile.closed? - if File.identical?(tmpfile, tmpfile.path) - unlinked = File.unlink tmpfile.path rescue nil - end - tmpfile.close - end - unless unlinked - begin - File.unlink tmpfile.path - rescue Errno::ENOENT - end - end - end - else - tmpfile - end -end