From d81020338c99ff4116431ee496a7db516eb91f00 Mon Sep 17 00:00:00 2001
From: Hisham Muhammad <hisham@gobolinux.org>
Date: Tue, 12 Dec 2023 20:40:14 -0300
Subject: improve rockspec.source.dir detection by moving it later (#1555)

---
 src/luarocks/build.lua           | 10 ++++-
 src/luarocks/cmd/new_version.lua | 16 ++++----
 src/luarocks/cmd/unpack.lua      |  7 ++--
 src/luarocks/fetch.lua           | 88 ++++++++++++++++++++++++++++++----------
 src/luarocks/rockspecs.lua       | 10 +----
 5 files changed, 88 insertions(+), 43 deletions(-)

(limited to 'src')

diff --git a/src/luarocks/build.lua b/src/luarocks/build.lua
index 0b82174b..42ae4591 100644
--- a/src/luarocks/build.lua
+++ b/src/luarocks/build.lua
@@ -159,8 +159,14 @@ local function fetch_and_change_to_source_dir(rockspec, opts)
       if not ok then
          return nil, err
       end
-   elseif rockspec.source.file then
-      local ok, err = fs.unpack_archive(rockspec.source.file)
+   else
+      if rockspec.source.file then
+         local ok, err = fs.unpack_archive(rockspec.source.file)
+         if not ok then
+            return nil, err
+         end
+      end
+      local ok, err = fetch.find_rockspec_source_dir(rockspec, ".")
       if not ok then
          return nil, err
       end
diff --git a/src/luarocks/cmd/new_version.lua b/src/luarocks/cmd/new_version.lua
index 49479910..ccba9335 100644
--- a/src/luarocks/cmd/new_version.lua
+++ b/src/luarocks/cmd/new_version.lua
@@ -78,16 +78,16 @@ local function check_url_and_update_md5(out_rs, invalid_is_error)
       util.warning("invalid URL - "..temp_dir)
       return true, false
    end
+   do
+      local inferred_dir, found_dir = fetch.find_base_dir(file, temp_dir, out_rs.source.url, out_rs.source.dir)
+      if not inferred_dir then
+         return nil, found_dir
+      end
 
-   local inferred_dir, found_dir = fetch.find_base_dir(file, temp_dir, out_rs.source.url, out_rs.source.dir)
-   if not inferred_dir then
-      return nil, found_dir
-   end
-
-   if found_dir and found_dir ~= inferred_dir then
-      out_rs.source.dir = found_dir
+      if found_dir and found_dir ~= inferred_dir then
+         out_rs.source.dir = found_dir
+      end
    end
-
    if file then
       if out_rs.source.md5 then
          util.printout("File successfully downloaded. Updating MD5 checksum...")
diff --git a/src/luarocks/cmd/unpack.lua b/src/luarocks/cmd/unpack.lua
index 94da2c9f..a0ade4f3 100644
--- a/src/luarocks/cmd/unpack.lua
+++ b/src/luarocks/cmd/unpack.lua
@@ -81,9 +81,9 @@ local function unpack_rock(rock_file, dir_name, kind)
    if kind == "src" then
       if rockspec.source.file then
          local ok, err = fs.unpack_archive(rockspec.source.file)
-         if not ok then
-            return nil, err
-         end
+         if not ok then return nil, err end
+         ok, err = fetch.find_rockspec_source_dir(rockspec, ".")
+         if not ok then return nil, err end
          ok, err = fs.change_dir(rockspec.source.dir)
          if not ok then return nil, err end
          ok, err = build.apply_patches(rockspec)
@@ -133,6 +133,7 @@ local function run_unpacker(file, force)
       return nil, err
    end
    if kind == "src" or kind == "rockspec" then
+      fetch.find_rockspec_source_dir(rockspec, ".")
       if rockspec.source.dir ~= "." then
          local ok = fs.copy(rockspec.local_abs_filename, rockspec.source.dir, "read")
          if not ok then
diff --git a/src/luarocks/fetch.lua b/src/luarocks/fetch.lua
index 90e7c79e..723af8b5 100644
--- a/src/luarocks/fetch.lua
+++ b/src/luarocks/fetch.lua
@@ -234,6 +234,21 @@ function fetch.find_base_dir(file, temp_dir, src_url, src_dir)
    local ok, err = fs.change_dir(temp_dir)
    if not ok then return nil, err end
    fs.unpack_archive(file)
+
+   if not src_dir then
+      local rockspec = {
+         source = {
+            file = file,
+            dir = src_dir,
+            url = src_url,
+         }
+      }
+      ok, err = fetch.find_rockspec_source_dir(rockspec, ".")
+      if ok then
+         src_dir = rockspec.source.dir
+      end
+   end
+
    local inferred_dir = src_dir or dir.deduce_base_dir(src_url)
    local found_dir = nil
    if fs.exists(inferred_dir) then
@@ -459,33 +474,58 @@ function fetch.get_sources(rockspec, extract, dest_dir)
       if not ok then return nil, err end
       ok, err = fs.unpack_archive(rockspec.source.file)
       if not ok then return nil, err end
-      if not fs.exists(rockspec.source.dir) then
-
-         -- If rockspec.source.dir can't be found, see if we only have one
-         -- directory in store_dir.  If that's the case, assume it's what
-         -- we're looking for.
-         -- We only do this if the rockspec source.dir was not set, and only
-         -- with rockspecs newer than 3.0.
-         local file_count, found_dir = 0
-
-         if not rockspec.source.dir_set and rockspec:format_is_at_least("3.0") then
-            for file in fs.dir() do
-               file_count = file_count + 1
-               if fs.is_dir(file) then
-                  found_dir = file
-               end
+      ok, err = fetch.find_rockspec_source_dir(rockspec, ".")
+      if not ok then return nil, err end
+      fs.pop_dir()
+   end
+   return source_file, store_dir
+end
+
+function fetch.find_rockspec_source_dir(rockspec, store_dir)
+   local ok, err = fs.change_dir(store_dir)
+   if not ok then return nil, err end
+
+   local file_count, dir_count, found_dir = 0, 0, 0
+
+   if rockspec.source.dir and fs.exists(rockspec.source.dir) then
+      ok, err = true, nil
+   elseif rockspec.source.file and rockspec.source.dir then
+      ok, err = nil, "Directory "..rockspec.source.dir.." not found inside archive "..rockspec.source.file
+   elseif not rockspec.source.dir_set then -- and rockspec:format_is_at_least("3.0") then
+
+      local name = dir.base_name(rockspec.source.file or rockspec.source.url or "")
+
+      if name:match("%.lua$") or name:match("%.c$") then
+         if fs.is_file(name) then
+            rockspec.source.dir = "."
+            ok, err = true, nil
+         end
+      end
+
+      if not rockspec.source.dir then
+         for file in fs.dir() do
+            file_count = file_count + 1
+            if fs.is_dir(file) then
+               dir_count = dir_count + 1
+               found_dir = file
             end
          end
 
-         if file_count == 1 and found_dir then
+         if dir_count == 1 then
             rockspec.source.dir = found_dir
+            ok, err = true, nil
          else
-            return nil, "Directory "..rockspec.source.dir.." not found inside archive "..rockspec.source.file, "source.dir", source_file, store_dir
+            ok, err = nil, "Could not determine source directory from rock contents (" .. tostring(file_count).." file(s), "..tostring(dir_count).." dir(s))"
          end
       end
-      fs.pop_dir()
+   else
+      ok, err = nil, "Could not determine source directory, please set source.dir in rockspec."
    end
-   return source_file, store_dir
+
+   fs.pop_dir()
+
+   assert(rockspec.source.dir or not ok)
+   return ok, err
 end
 
 --- Download sources for building a rock, calling the appropriate protocol method.
@@ -511,7 +551,7 @@ function fetch.fetch_sources(rockspec, extract, dest_dir)
    end
 
    local protocol = rockspec.source.protocol
-   local ok, proto
+   local ok, err, proto
    if dir.is_basic_protocol(protocol) then
       proto = fetch
    else
@@ -531,7 +571,13 @@ function fetch.fetch_sources(rockspec, extract, dest_dir)
       end
    end
 
-   return proto.get_sources(rockspec, extract, dest_dir)
+   local source_file, store_dir = proto.get_sources(rockspec, extract, dest_dir)
+   if not source_file then return nil, store_dir end
+
+   ok, err = fetch.find_rockspec_source_dir(rockspec, store_dir)
+   if not ok then return nil, err, "source.dir", source_file, store_dir end
+
+   return source_file, store_dir
 end
 
 return fetch
diff --git a/src/luarocks/rockspecs.lua b/src/luarocks/rockspecs.lua
index c9e17530..454bab77 100644
--- a/src/luarocks/rockspecs.lua
+++ b/src/luarocks/rockspecs.lua
@@ -140,16 +140,8 @@ function rockspecs.from_persisted_table(filename, rockspec, globals, quick)
    if rockspec.source.cvs_tag then rockspec.source.tag = rockspec.source.cvs_tag end
 
    rockspec.local_abs_filename = filename
-   local filebase = rockspec.source.file or rockspec.source.url
-   local base = dir.deduce_base_dir(filebase)
    rockspec.source.dir_set = rockspec.source.dir ~= nil
-   rockspec.source.dir = rockspec.source.dir
-                      or rockspec.source.module
-                      or ( (filebase:match("%.lua$") or filebase:match("%.c$"))
-                           and (rockspec:format_is_at_least("3.0")
-                                and (dir.is_basic_protocol(protocol) and "." or base)
-                                or  ".") )
-                      or base
+   rockspec.source.dir = rockspec.source.dir or rockspec.source.module
 
    rockspec.rocks_provided = util.get_rocks_provided(rockspec)
 
-- 
cgit v1.2.3-55-g6feb