Repository: conan-io/conan

Issue:
Lib order in conanbuildinfo.props is generated randomly every time
To help us debug your issue please explain:

- [x] I've read the [CONTRIBUTING guide](https://github.com/conan-io/conan/blob/develop/.github/CONTRIBUTING.md).
- [x] I've specified the Conan version, operating system version and any tool that can be relevant.
- [x] I've explained the steps to reproduce the error or the motivation/use case of the question/suggestion.

I'm using conan-1.12.2 on Windows-10.

conanfile.py
```
from conans import ConanFile

class MyConan(ConanFile):
	settings = "os", "compiler", "build_type", "arch"

	requires = (
		"OpenSSL/1.0.2q@conan/stable",
		"boost/1.69.0@conan/stable",
		"cpprestsdk/2.10.10@bincrafters/stable",
		"libzip/1.5.1@bincrafters/stable",
		"zlib/1.2.11@conan/stable",
		"gsl_microsoft/2.0.0@bincrafters/stable",
	)
	
	generators = "visual_studio"
	
	default_options = (
		"libzip:shared=True",
		"zlib:shared=True"
	)
		
	def imports(self):
		self.copy("*.dll", "..\\..\\" + str(self.settings.build_type), "bin")

```

The order of libboost_math libs is different each time I run `conan install`. It is annoying when using source control tool.
```
-      <AdditionalDependencies>libboost_wave.lib;libboost_contract.lib;libboost_graph.lib;libboost_iostreams.lib;libboost_log.lib;libboost_program_options.lib;libboost_serialization.lib;libboost_wserialization.lib;libboost_fiber.lib;libboost_timer.lib;libboost_type_erasure.lib;libboost_log_setup.lib;libboost_stacktrace_noop.lib;libboost_stacktrace_windbg.lib;libboost_stacktrace_windbg_cached.lib;libboost_unit_test_framework.lib;cpprest140_2_10.lib;winhttp.lib;httpapi.lib;bcrypt.lib;zip.lib;ssleay32.lib;libeay32.lib;crypt32.lib;msi.lib;ws2_32.lib;libboost_random.lib;libboost_date_time.lib;libboost_coroutine.lib;libboost_context.lib;libboost_thread.lib;zlib.lib;bz2.lib;libboost_filesystem.lib;libboost_chrono.lib;libboost_locale.lib;libboost_math_c99l.lib;libboost_math_c99f.lib;libboost_math_tr1l.lib;libboost_math_tr1.lib;libboost_math_tr1f.lib;libboost_math_c99.lib;liblzma.lib;zstd_static.lib;libboost_system.lib;libboost_atomic.lib;libboost_regex.lib;libboost_exception.lib;libboost_container.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>libboost_wave.lib;libboost_contract.lib;libboost_graph.lib;libboost_iostreams.lib;libboost_log.lib;libboost_program_options.lib;libboost_serialization.lib;libboost_wserialization.lib;libboost_fiber.lib;libboost_timer.lib;libboost_type_erasure.lib;libboost_log_setup.lib;libboost_stacktrace_noop.lib;libboost_stacktrace_windbg.lib;libboost_stacktrace_windbg_cached.lib;libboost_unit_test_framework.lib;cpprest140_2_10.lib;winhttp.lib;httpapi.lib;bcrypt.lib;zip.lib;ssleay32.lib;libeay32.lib;crypt32.lib;msi.lib;ws2_32.lib;libboost_random.lib;libboost_date_time.lib;libboost_coroutine.lib;libboost_context.lib;libboost_thread.lib;zlib.lib;bz2.lib;libboost_filesystem.lib;libboost_chrono.lib;libboost_locale.lib;libboost_math_c99f.lib;libboost_math_c99l.lib;libboost_math_tr1f.lib;libboost_math_tr1l.lib;libboost_math_c99.lib;libboost_math_tr1.lib;liblzma.lib;zstd_static.lib;libboost_system.lib;libboost_atomic.lib;libboost_regex.lib;libboost_exception.lib;libboost_container.lib;%(AdditionalDependencies)</AdditionalDependencies>

```


Discussion:
Hi @sinall 

Thanks for reporting this issue.

It seems that this is mostly a boost package issue, not a conan issue, but I would discuss here and wait for feedback:

- The boost package is using ``tools.collect_libs()``, which internally uses `` files = os.listdir(lib_folder)``, which file order is not guaranteed.
- The boost package is later iterating like this:
```python
        gen_libs = tools.collect_libs(self)

        # List of lists, so if more than one matches the lib like serialization and wserialization
        # both will be added to the list
        ordered_libs = [[] for _ in range(len(lib_list))]

        # The order is important, reorder following the lib_list order
        missing_order_info = []
        for real_lib_name in gen_libs:
            for pos, alib in enumerate(lib_list):
                if os.path.splitext(real_lib_name)[0].split("-")[0].endswith(alib):
                    ordered_libs[pos].append(real_lib_name)
                    break
```

Possible fixes:
- Change the boost recipe and make it use the same order always.
- Fix the ``tools.collect_libs()`` to do a ``sort(os.listdir())`` while iterating, to force it to return always the same order.

Will discuss with @lasote

I'm ok ordering the list of `collect_libs()`.
Perfect, yes, I agree, having a deterministic collect_libs() make sense.

Resolving patch:
diff --git a/conans/client/tools/files.py b/conans/client/tools/files.py
--- a/conans/client/tools/files.py
+++ b/conans/client/tools/files.py
@@ -322,6 +322,7 @@ def collect_libs(conanfile, folder=None):
                                           "times with a different file extension" % name)
                 else:
                     result.append(name)
+    result.sort()
     return result
 
 
@@ -369,5 +370,3 @@ def unix2dos(filepath):
 
 def dos2unix(filepath):
     _replace_with_separator(filepath, "\n")
-
-


Test patch:
diff --git a/conans/test/unittests/util/tools_test.py b/conans/test/unittests/util/tools_test.py
--- a/conans/test/unittests/util/tools_test.py
+++ b/conans/test/unittests/util/tools_test.py
@@ -632,7 +632,7 @@ def build(self):
 
     def test_global_tools_overrided(self):
         client = TestClient()
- 
+
         conanfile = """
 from conans import ConanFile, tools
 
@@ -960,17 +960,17 @@ def vcvars_dict_test(self):
 PROCESSOR_IDENTIFIER=Intel64 Family 6 Model 158 Stepping 9, GenuineIntel
 
 
- PROCESSOR_LEVEL=6 
+ PROCESSOR_LEVEL=6
+
+PROCESSOR_REVISION=9e09
 
-PROCESSOR_REVISION=9e09    
 
-                         
 set nl=^
 env_var=
 without_equals_sign
 
 ProgramFiles(x86)=C:\Program Files (x86)
-       
+
 """.encode("utf-8")
 
         def vcvars_command_mock(settings, arch, compiler_version, force, vcvars_ver, winsdk_version,
@@ -1168,15 +1168,15 @@ def get_values(this_os, this_arch, setting_os, setting_arch, compiler=None):
         build, host = get_values("Linux", "x86_64", "Linux", "armv8_32")
         self.assertEqual(build, "x86_64-linux-gnu")
         self.assertEqual(host, "aarch64-linux-gnu_ilp32")
-        
+
         build, host = get_values("Linux", "x86_64", "Linux", "armv5el")
         self.assertEqual(build, "x86_64-linux-gnu")
         self.assertEqual(host, "arm-linux-gnueabi")
-        
+
         build, host = get_values("Linux", "x86_64", "Linux", "armv5hf")
         self.assertEqual(build, "x86_64-linux-gnu")
         self.assertEqual(host, "arm-linux-gnueabihf")
-       
+
         build, host = get_values("Linux", "x86_64", "Android", "x86")
         self.assertEqual(build, "x86_64-linux-gnu")
         self.assertEqual(host, "i686-linux-android")
@@ -2268,7 +2268,7 @@ def collect_libs_test(self):
         # Use cpp_info.libdirs
         conanfile.cpp_info.libdirs = ["lib", "custom_folder"]
         result = tools.collect_libs(conanfile)
-        self.assertEqual(["mylib", "customlib"], result)
+        self.assertEqual(["customlib", "mylib"], result)
 
         # Custom folder with multiple libdirs should only collect from custom folder
         self.assertEqual(["lib", "custom_folder"], conanfile.cpp_info.libdirs)
@@ -2334,7 +2334,7 @@ def self_collect_libs_test(self):
         # Use cpp_info.libdirs
         conanfile.cpp_info.libdirs = ["lib", "custom_folder"]
         result = conanfile.collect_libs()
-        self.assertEqual(["mylib", "customlib"], result)
+        self.assertEqual(["customlib", "mylib"], result)
 
         # Custom folder with multiple libdirs should only collect from custom folder
         self.assertEqual(["lib", "custom_folder"], conanfile.cpp_info.libdirs)