[前][次][番号順一覧][スレッド一覧]

ruby-changes:49907

From: k0kubun <ko1@a...>
Date: Wed, 24 Jan 2018 23:11:31 +0900 (JST)
Subject: [ruby-changes:49907] k0kubun:r62025 (trunk): process.c: add :exception option to Kernel.#system

k0kubun	2018-01-24 23:11:25 +0900 (Wed, 24 Jan 2018)

  New Revision: 62025

  https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=62025

  Log:
    process.c: add :exception option to Kernel.#system
    
    to raise error when it fails.
    
    [Feature 14386] [GH-1795]

  Modified files:
    trunk/ext/pty/pty.c
    trunk/internal.h
    trunk/io.c
    trunk/process.c
    trunk/test/ruby/test_system.rb
Index: test/ruby/test_system.rb
===================================================================
--- test/ruby/test_system.rb	(revision 62024)
+++ test/ruby/test_system.rb	(revision 62025)
@@ -160,4 +160,23 @@ class TestSystem < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_system.rb#L160
       assert_equal(true, system(tmpfilename), '[ruby-core:32745]')
     }
   end if File.executable?("/bin/sh")
+
+  def test_system_exception
+    ruby = EnvUtil.rubybin
+    assert_nothing_raised do
+      system('feature_14235', exception: false)
+      system("'#{ruby}' -e 'exit 1'", exception: false)
+    end
+    assert_raise(Errno::ENOENT) do
+      system('feature_14235', exception: true)
+    end
+    assert_raise(RuntimeError) do
+      system("'#{ruby}' -e 'exit 1'", exception: true)
+    end
+    begin
+      system("'#{ruby}' -e 'exit 1'", exception: true)
+    rescue RuntimeError => e
+      assert_equal true, e.message.include?('status (1)')
+    end
+  end
 end
Index: internal.h
===================================================================
--- internal.h	(revision 62024)
+++ internal.h	(revision 62025)
@@ -1631,6 +1631,7 @@ struct rb_execarg { https://github.com/ruby/ruby/blob/trunk/internal.h#L1631
     unsigned new_pgroup_flag : 1;
     unsigned uid_given : 1;
     unsigned gid_given : 1;
+    unsigned exception : 1;
     rb_pid_t pgroup_pgid; /* asis(-1), new pgroup(0), specified pgroup (0<V). */
     VALUE rlimit_limits; /* Qfalse or [[rtype, softlim, hardlim], ...] */
     mode_t umask_mask;
@@ -1968,9 +1969,9 @@ VALUE rb_int_positive_pow(long x, unsign https://github.com/ruby/ruby/blob/trunk/internal.h#L1969
 /* process.c (export) */
 int rb_exec_async_signal_safe(const struct rb_execarg *e, char *errmsg, size_t errmsg_buflen);
 rb_pid_t rb_fork_async_signal_safe(int *status, int (*chfunc)(void*, char *, size_t), void *charg, VALUE fds, char *errmsg, size_t errmsg_buflen);
-VALUE rb_execarg_new(int argc, const VALUE *argv, int accept_shell);
+VALUE rb_execarg_new(int argc, const VALUE *argv, int accept_shell, int allow_exc_opt);
 struct rb_execarg *rb_execarg_get(VALUE execarg_obj); /* dangerous.  needs GC guard. */
-VALUE rb_execarg_init(int argc, const VALUE *argv, int accept_shell, VALUE execarg_obj);
+VALUE rb_execarg_init(int argc, const VALUE *argv, int accept_shell, VALUE execarg_obj, int allow_exc_opt);
 int rb_execarg_addopt(VALUE execarg_obj, VALUE key, VALUE val);
 void rb_execarg_parent_start(VALUE execarg_obj);
 void rb_execarg_parent_end(VALUE execarg_obj);
Index: ext/pty/pty.c
===================================================================
--- ext/pty/pty.c	(revision 62024)
+++ ext/pty/pty.c	(revision 62025)
@@ -182,7 +182,7 @@ establishShell(int argc, VALUE *argv, st https://github.com/ruby/ruby/blob/trunk/ext/pty/pty.c#L182
 	argv = &v;
     }
 
-    carg.execarg_obj = rb_execarg_new(argc, argv, 1);
+    carg.execarg_obj = rb_execarg_new(argc, argv, 1, 0);
     carg.eargp = rb_execarg_get(carg.execarg_obj);
     rb_execarg_parent_start(carg.execarg_obj);
 
Index: io.c
===================================================================
--- io.c	(revision 62024)
+++ io.c	(revision 62025)
@@ -6633,7 +6633,7 @@ pipe_open_s(VALUE prog, const char *mode https://github.com/ruby/ruby/blob/trunk/io.c#L6633
     VALUE execarg_obj = Qnil;
 
     if (!is_popen_fork(prog))
-	execarg_obj = rb_execarg_new(argc, argv, TRUE);
+        execarg_obj = rb_execarg_new(argc, argv, TRUE, FALSE);
     return pipe_open(execarg_obj, modestr, fmode, convconfig);
 }
 
@@ -6766,14 +6766,14 @@ rb_io_s_popen(int argc, VALUE *argv, VAL https://github.com/ruby/ruby/blob/trunk/io.c#L6766
 	    rb_raise(rb_eArgError, "too many arguments");
 	}
 #endif
-	execarg_obj = rb_execarg_new((int)len, RARRAY_CONST_PTR(tmp), FALSE);
+        execarg_obj = rb_execarg_new((int)len, RARRAY_CONST_PTR(tmp), FALSE, FALSE);
 	RB_GC_GUARD(tmp);
     }
     else {
 	SafeStringValue(pname);
 	execarg_obj = Qnil;
 	if (!is_popen_fork(pname))
-	    execarg_obj = rb_execarg_new(1, &pname, TRUE);
+            execarg_obj = rb_execarg_new(1, &pname, TRUE, FALSE);
     }
     if (!NIL_P(execarg_obj)) {
 	if (!NIL_P(opt))
Index: process.c
===================================================================
--- process.c	(revision 62024)
+++ process.c	(revision 62025)
@@ -249,7 +249,7 @@ typedef unsigned LONG_LONG unsigned_cloc https://github.com/ruby/ruby/blob/trunk/process.c#L249
 typedef void (*sig_t) (int);
 #endif
 
-static ID id_in, id_out, id_err, id_pid, id_uid, id_gid;
+static ID id_in, id_out, id_err, id_pid, id_uid, id_gid, id_exception;
 static ID id_close, id_child;
 #ifdef HAVE_SETPGID
 static ID id_pgroup;
@@ -2253,12 +2253,12 @@ rb_exec_fillarg(VALUE prog, int argc, VA https://github.com/ruby/ruby/blob/trunk/process.c#L2253
 }
 
 VALUE
-rb_execarg_new(int argc, const VALUE *argv, int accept_shell)
+rb_execarg_new(int argc, const VALUE *argv, int accept_shell, int allow_exc_opt)
 {
     VALUE execarg_obj;
     struct rb_execarg *eargp;
     execarg_obj = TypedData_Make_Struct(0, struct rb_execarg, &exec_arg_data_type, eargp);
-    rb_execarg_init(argc, argv, accept_shell, execarg_obj);
+    rb_execarg_init(argc, argv, accept_shell, execarg_obj, allow_exc_opt);
     return execarg_obj;
 }
 
@@ -2271,16 +2271,23 @@ rb_execarg_get(VALUE execarg_obj) https://github.com/ruby/ruby/blob/trunk/process.c#L2271
 }
 
 VALUE
-rb_execarg_init(int argc, const VALUE *orig_argv, int accept_shell, VALUE execarg_obj)
+rb_execarg_init(int argc, const VALUE *orig_argv, int accept_shell, VALUE execarg_obj, int allow_exc_opt)
 {
     struct rb_execarg *eargp = rb_execarg_get(execarg_obj);
-    VALUE prog, ret;
+    VALUE prog, ret, exception = Qnil;
     VALUE env = Qnil, opthash = Qnil;
     VALUE argv_buf;
     VALUE *argv = ALLOCV_N(VALUE, argv_buf, argc);
     MEMCPY(argv, orig_argv, VALUE, argc);
     prog = rb_exec_getargs(&argc, &argv, accept_shell, &env, &opthash);
+    if (allow_exc_opt && !NIL_P(opthash) && rb_hash_has_key(opthash, ID2SYM(id_exception))) {
+        opthash = rb_hash_dup(opthash);
+        exception = rb_hash_delete(opthash, ID2SYM(id_exception));
+    }
     rb_exec_fillarg(prog, argc, argv, env, opthash, execarg_obj);
+    if (RTEST(exception)) {
+        eargp->exception = 1;
+    }
     ALLOCV_END(argv_buf);
     ret = eargp->use_shell ? eargp->invoke.sh.shell_script : eargp->invoke.cmd.command_name;
     RB_GC_GUARD(execarg_obj);
@@ -2599,7 +2606,7 @@ rb_f_exec(int argc, const VALUE *argv) https://github.com/ruby/ruby/blob/trunk/process.c#L2606
     char errmsg[CHILD_ERRMSG_BUFLEN] = { '\0' };
     int err;
 
-    execarg_obj = rb_execarg_new(argc, argv, TRUE);
+    execarg_obj = rb_execarg_new(argc, argv, TRUE, FALSE);
     eargp = rb_execarg_get(execarg_obj);
     before_exec(); /* stop timer thread before redirects */
     rb_execarg_parent_start(execarg_obj);
@@ -3989,7 +3996,7 @@ rb_spawn_internal(int argc, const VALUE https://github.com/ruby/ruby/blob/trunk/process.c#L3996
 {
     VALUE execarg_obj;
 
-    execarg_obj = rb_execarg_new(argc, argv, TRUE);
+    execarg_obj = rb_execarg_new(argc, argv, TRUE, FALSE);
     return rb_execarg_spawn(execarg_obj, errmsg, errmsg_buflen);
 }
 
@@ -4043,6 +4050,8 @@ rb_f_system(int argc, VALUE *argv) https://github.com/ruby/ruby/blob/trunk/process.c#L4050
 {
     rb_pid_t pid;
     int status;
+    VALUE execarg_obj;
+    struct rb_execarg *eargp;
 
 #if defined(SIGCLD) && !defined(SIGCHLD)
 # define SIGCHLD SIGCLD
@@ -4054,7 +4063,8 @@ rb_f_system(int argc, VALUE *argv) https://github.com/ruby/ruby/blob/trunk/process.c#L4063
     rb_last_status_clear();
     chfunc = signal(SIGCHLD, SIG_DFL);
 #endif
-    pid = rb_spawn_internal(argc, argv, NULL, 0);
+    execarg_obj = rb_execarg_new(argc, argv, TRUE, TRUE);
+    pid = rb_execarg_spawn(execarg_obj, NULL, 0);
 #if defined(HAVE_WORKING_FORK) || defined(HAVE_SPAWNV)
     if (pid > 0) {
         int ret, status;
@@ -4066,12 +4076,26 @@ rb_f_system(int argc, VALUE *argv) https://github.com/ruby/ruby/blob/trunk/process.c#L4076
 #ifdef SIGCHLD
     signal(SIGCHLD, chfunc);
 #endif
+    TypedData_Get_Struct(execarg_obj, struct rb_execarg, &exec_arg_data_type, eargp);
     if (pid < 0) {
-	return Qnil;
+        if (eargp->exception) {
+            int err = errno;
+            rb_syserr_fail_str(err, eargp->invoke.sh.shell_script);
+            RB_GC_GUARD(execarg_obj);
+        }
+        else {
+            return Qnil;
+        }
     }
     status = PST2INT(rb_last_status_get());
     if (status == EXIT_SUCCESS) return Qtrue;
-    return Qfalse;
+    if (eargp->exception) {
+        rb_raise(rb_eRuntimeError, "Command failed with status (%d): %s",
+                 WEXITSTATUS(status), RSTRING_PTR(eargp->invoke.sh.shell_script));
+    }
+    else {
+        return Qfalse;
+    }
 }
 
 /*
@@ -4350,7 +4374,7 @@ rb_f_spawn(int argc, VALUE *argv) https://github.com/ruby/ruby/blob/trunk/process.c#L4374
     VALUE execarg_obj, fail_str;
     struct rb_execarg *eargp;
 
-    execarg_obj = rb_execarg_new(argc, argv, TRUE);
+    execarg_obj = rb_execarg_new(argc, argv, TRUE, FALSE);
     eargp = rb_execarg_get(execarg_obj);
     fail_str = eargp->use_shell ? eargp->invoke.sh.shell_script : eargp->invoke.cmd.command_name;
 
@@ -8038,6 +8062,7 @@ Init_process(void) https://github.com/ruby/ruby/blob/trunk/process.c#L8062
     id_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC = rb_intern("MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC");
 #endif
     id_hertz = rb_intern("hertz");
+    id_exception = rb_intern("exception");
 
     InitVM(process);
 }

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]