     Enum.all?(keys, &(Keyword.fetch(opts1, &1) == Keyword.fetch(opts2, &1)))
   end
 
+  defp reject_non_fullfilled_optional(deps) do
+    apps = Enum.map(deps, & &1.app)
+    for dep <- deps do
+      update_in dep.deps, &reject_non_fullfilled_optional(&1, apps)
+    end
+  end
+
   defp reject_non_fullfilled_optional(children, upper_breadths) do
     Enum.reject children, fn %Mix.Dep{app: app, opts: opts} ->
       opts[:optional] && not(app in upper_breadths)

              {Mix.Dep.Loader.load(dep, children), rest, lock}
          end

        dep = %{dep | deps: reject_non_fullfilled_optional(dep.deps, current_breadths)}
        {acc, rest, lock} =
          all(t, [dep|acc], upper_breadths, current_breadths, callback, rest, lock, env, cache)

        new_breadths = Enum.map(dep.deps, &(&1.app)) ++ current_breadths
        all(dep.deps, acc, current_breadths, new_breadths, callback, rest, lock, env, cache)
    end
  end


      # In case no lock was given, we will use the local lock
      # which is potentially stale. So remote.deps/2 needs to always
      # check if the data it finds in the lock is actually valid.
      {deps, rest, lock} =
        all(main, apps, callback, rest, lock, env, fn dep ->
          if cached = deps[dep.app] do
            {:loaded, cached}
          else
            {:unloaded, dep, remote.deps(dep, lock)}
          end
        end)

      {reject_non_fullfilled_optional(deps), rest, lock}
    else
      {reject_non_fullfilled_optional(deps), rest, lock}
    end
  end

