Commits

Jason Perkins committed 85c1427

Prevent single-child ".." folders from appearing at the top of the source tree; merge project.eachfile() into project.getsourcetree()

Comments (0)

Files changed (5)

src/base/tree.lua

 	function tree.trimroot(tr)
 		local trimmed
 		
+		-- start by removing single-children folders from the top of the tree
 		while #tr.children == 1 do
 			local node = tr.children[1]
 			
 			end			
 			
 			-- remove this node from the tree, and move its children up a level
-			trimmed = node.path
-			
+			trimmed = true			
 			local numChildren = #node.children
 			for i = 1, numChildren do
 				local child = node.children[i]
 			end
 		end
 		
+		-- found the top, now remove any single-children ".." folders from here
+		local dotdot
+		local count = #tr.children
+		repeat
+			dotdot = false
+			for i = 1, count do
+				local node = tr.children[i]
+				if node.name == ".." and #node.children == 1 then
+					local child = node.children[1]
+					child.parent = node.parent
+					tr.children[i] = child
+					trimmed = true
+					dotdot = true
+				end
+			end
+		until not dotdot
+				
 		-- if nodes were removed, adjust the paths on all remaining nodes
 		if trimmed then
-			local trimlen = #trimmed + 2
 			tree.traverse(tr, {
 				onnode = function(node)
-					node.path = node.path:sub(trimlen)
+					if node.parent.path then
+						node.path = path.join(node.parent.path, node.name)
+					else
+						node.path = node.name
+					end
 				end
-			})
+			}, false)
 		end
 	end

src/project/project.lua

 	end
 
 
---
--- Return an iterator for the list of source code files contained by a project.
---
--- @param prj
---    The project to query.
--- @return
---    A source code file iterator, which returns file configuration objects.
---    These file configurations contain:
---
---      fullpath  - the relative path from the project to the file
---      vpath     - the file's virtual path, if specified, or fullpath if not
---
-
-	function project.eachfile(prj)
-		-- make sure I have the project, and not it's root configuration
-		prj = prj.project or prj
-		
-		-- find *all* files referenced by the project, regardless of configuration,
-		-- and cache the list for future calls
-		if not prj.files then
-			local files = {}
-			for _, block in ipairs(prj.blocks) do
-				for _, file in ipairs(block.files) do
-					if not files[file] then						
-						local fcfg = project.getfileconfig(prj, file)
-					
-						-- add it both indexed for iteration and keyed for quick tests
-						table.insert(files, file)
-						files[file] = fcfg
-					end
-				end
-			end
-			prj.files = files
-		end
-			
-		local files = prj.files
-		local i = 0
-		
-		return function()
-			i = i + 1
-			if i <= #files then
-				local filename = files[i]
-				return files[filename]
-			end
-		end
-	end
-
-
 -- 
 -- Locate a project by name; case insensitive.
 --
 --
 
 	function project.getsourcetree(prj)
+		-- make sure I have the project, and not it's root configuration
+		prj = prj.project or prj
+		
 		-- check for a previously cached tree
 		if prj.sourcetree then
 			return prj.sourcetree
 		end
+
+		-- find *all* files referenced by the project, regardless of configuration
+		local files = {}
+		for _, block in ipairs(prj.blocks) do
+			for _, file in ipairs(block.files) do
+				files[file] = file
+			end
+		end
+
+		-- create a tree from the file list
+		local tr = premake.tree.new(prj.name)
 		
-		local tr = premake.tree.new(prj.name)
+		for file in pairs(files) do
+			local fcfg = project.getfileconfig(prj, file)
 
-		for fcfg in project.eachfile(prj) do
 			-- The tree represents the logical source code tree to be displayed
 			-- in the IDE, not the physical organization of the file system. So
 			-- virtual paths are used when adding nodes.
 				end
 			end)
 
-			-- Store additional path information for file (leaf) nodes
-			node.abspath = fcfg.abspath
-			node.relpath = fcfg.relpath
-			node.vpath = fcfg.vpath
+			-- Store full file configuration in file (leaf) nodes
+			for key, value in pairs(fcfg) do
+				node[key] = value
+			end
 		end
 
 		premake.tree.trimroot(tr)

tests/base/test_tree.lua

 
 
 --
+-- A ".." folder containing a single subfolder should never appear
+-- at the top of the source tree.
+--
+
+	function suite.trimroot_removesDotDot_onTopLevelSiblings()
+		tree.add(tr, "../../tests/test_hello.c")
+		tree.add(tr, "../src/test.c")
+		tree.trimroot(tr)
+		prepare()
+		test.capture [[
+		tests
+			test_hello.c
+		src
+			test.c
+		]]
+	end
+
+	function suite.trimroot_removesDotDot_onTopLevel()
+		tree.add(tr, "../tests/test_hello.c")
+		tree.add(tr, "src/test.c")
+		tree.trimroot(tr)
+		prepare()
+		test.capture [[
+		tests
+			test_hello.c
+		src
+			test.c
+		]]
+	end
+
+	function suite.trimroot_removesDotDot_onMultipleNestings()
+		tree.add(tr, "../../../tests/test_hello.c")
+		tree.add(tr, "../src/test.c")
+		tree.trimroot(tr)
+		prepare()
+		test.capture [[
+		tests
+			test_hello.c
+		src
+			test.c
+		]]
+	end
+
+
+--
 -- When nodes are trimmed, the paths on the remaining nodes should
 -- be updated to reflect the new hierarchy.
 --
 		tree.add(tr, "A/1")
 		tree.add(tr, "A/2")
 		tree.trimroot(tr)
-		prepare()
 		test.isequal("1", tr.children[1].path)
 	end
+
+
+	function suite.trimroot_updatesPaths_onDotDotRemoved()
+		tree.add(tr, "../../../tests/test_hello.c")
+		tree.add(tr, "../src/test.c")
+		tree.trimroot(tr)
+		test.isequal("tests", tr.children[1].path)
+	end

tests/premake4.lua

 	dofile("test_project.lua")
 	dofile("project/test_baking.lua")
 	dofile("project/test_eachconfig.lua")
-	dofile("project/test_eachfile.lua")
 	dofile("project/test_filtering.lua")
 	dofile("project/test_getconfig.lua")
 	dofile("project/test_hasconfig.lua")

tests/project/test_eachfile.lua

---
--- tests/project/test_eachfile.lua
--- Automated test suite for the file iteration function.
--- Copyright (c) 2011-2012 Jason Perkins and the Premake project
---
-
-	T.project_eachfile = { }
-	local suite = T.project_eachfile
-	local project = premake5.project
-
-
---
--- Setup and teardown
---
-
-	local sln, prj
-	function suite.setup()
-		sln, prj = test.createsolution()
-	end
-
-	local function prepare(field)
-		if not field then
-			field = "relpath"
-		end		
-		for file in project.eachfile(prj) do
-			_p(2, file[field])
-		end
-	end
-
-
---
--- Sanity check that all files are returned, with project relative paths.
---
-
-	function suite.listsAllFiles()
-		files { "hello.h", "hello.c" }
-		prepare()
-		test.capture [[
-		hello.h
-		hello.c
-		]]
-	end
-
---
--- Ensure that the virtual path field defaults to the real file path.
---
-
-	function suite.vpathsAreNil_onNoVpaths()
-		files { "hello.h", "hello.c" }
-		prepare("vpath")
-		test.capture [[
-		hello.h
-		hello.c
-		]]
-	end
-
---
--- If a virtual path is specified, the vpath field should be set.
---
-
-	function suite.vpathSet_onVpath()
-		files { "hello.h", "hello.c" }
-		vpaths { Headers = "**.h" }
-		prepare("vpath")
-		test.capture [[
-		Headers/hello.h
-		hello.c
-		]]
-	end
-
---
--- A file listed in a configuration should be included in the list.
---
-
-	function suite.includesFile_setInConfiguration()
-		files { "hello.h" }
-		configuration { "Debug" }
-		files { "hello.c" }
-		prepare()
-		test.capture [[
-		hello.h
-		hello.c
-		]]
-	end